/*
Copyright (C) 2014 William Hart
This file is part of FLINT.
FLINT is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See .
*/
#define ulong ulongxx /* interferes with system includes */
#include
#include
#include
#undef ulong
#define ulong mp_limb_t
#include
#include "flint.h"
#include "ulong_extras.h"
#include "fmpz.h"
#include "fmpz_vec.h"
void fmpz_lucas_chain(fmpz_t Vm, fmpz_t Vm1, const fmpz_t A,
const fmpz_t m, const fmpz_t n)
{
fmpz_t t;
slong i, B = fmpz_sizeinbase(m, 2);
fmpz_init(t);
fmpz_set_ui(Vm, 2);
fmpz_set(Vm1, A);
for (i = B - 1; i >= 0; i--)
{
if (fmpz_tstbit(m, i)) /* 1 in binary repn */
{
fmpz_mul(t, Vm, Vm1);
fmpz_sub(t, t, A);
fmpz_mod(Vm, t, n);
fmpz_mul(t, Vm1, Vm1);
fmpz_sub_ui(t, t, 2);
fmpz_mod(Vm1, t, n);
} else /* 0 in binary repn */
{
fmpz_mul(t, Vm, Vm1);
fmpz_sub(t, t, A);
fmpz_mod(Vm1, t, n);
fmpz_mul(t, Vm, Vm);
fmpz_sub_ui(t, t, 2);
fmpz_mod(Vm, t, n);
}
}
fmpz_clear(t);
}
void fmpz_lucas_chain_full(fmpz_t Vm, fmpz_t Vm1, const fmpz_t A, const fmpz_t B,
const fmpz_t m, const fmpz_t n)
{
fmpz_t t, Q;
slong i, bits = fmpz_sizeinbase(m, 2);
fmpz_init(t);
fmpz_init(Q);
fmpz_set_ui(Q, 1);
fmpz_set_ui(Vm, 2);
fmpz_set(Vm1, A);
for (i = bits - 1; i >= 0; i--)
{
if (fmpz_tstbit(m, i)) /* 1 in binary repn */
{
fmpz_mul(t, Vm1, Vm);
fmpz_submul(t, Q, A);
fmpz_mod(Vm, t, n);
fmpz_mul(Vm1, Vm1, Vm1);
fmpz_mul_ui(t, Q, 2);
fmpz_mul(t, t, B);
fmpz_sub(Vm1, Vm1, t);
fmpz_mod(Vm1, Vm1, n);
fmpz_mul(Q, Q, Q);
fmpz_mul(Q, Q, B);
fmpz_mod(Q, Q, n);
} else /* 0 in binary repn */
{
fmpz_mul(t, Vm, Vm1);
fmpz_submul(t, Q, A);
fmpz_mod(Vm1, t, n);
fmpz_mul(t, Vm, Vm);
fmpz_submul_ui(t, Q, 2);
fmpz_mod(Vm, t, n);
fmpz_mul(Q, Q, Q);
fmpz_mod(Q, Q, n);
}
}
fmpz_clear(Q);
fmpz_clear(t);
}
/* Compute U_{2m}, U_{2m + 1} given U_m, U_{m + 1} */
void fmpz_lucas_chain_double(fmpz_t U2m, fmpz_t U2m1, const fmpz_t Um,
const fmpz_t Um1, const fmpz_t A, const fmpz_t B,
const fmpz_t n)
{
fmpz_t t, t2;
fmpz_init(t);
fmpz_init(t2);
fmpz_mul_2exp(t, Um1, 1); /* U_m(2U_{m+1) - AU_m) */
fmpz_submul(t, Um, A);
fmpz_mul(t, t, Um);
fmpz_mul(U2m1, Um1, Um1); /* U_{m+1}^2 - BU_m^2 */
fmpz_mul(t2, Um, Um);
fmpz_submul(U2m1, t2, B);
fmpz_mod(U2m1, U2m1, n);
fmpz_mod(U2m, t, n);
fmpz_clear(t);
fmpz_clear(t2);
}
/*
Compute U_{m + n}, U_{m + n + 1} given U_m, U_{m + 1} and
U_n, U_{n + 1}
*/
void fmpz_lucas_chain_add(fmpz_t Umn, fmpz_t Umn1, const fmpz_t Um,
const fmpz_t Um1, const fmpz_t Un,
const fmpz_t Un1, const fmpz_t A, const fmpz_t B,
const fmpz_t n)
{
fmpz_t t, t2;
fmpz_init(t);
fmpz_init(t2);
fmpz_mul(t, Un, A); /* U_nU_{m + 1} - BU_m(AU_n - U_{n + 1})/B */
fmpz_sub(t, Un1, t);
fmpz_mul(t, t, Um);
fmpz_addmul(t, Un, Um1);
fmpz_mul(Umn1, Un1, Um1); /* U_{n + 1}U_{m + 1} - BU_mU_n */
fmpz_mul(t2, Um, Un);
fmpz_submul(Umn1, t2, B);
fmpz_mod(Umn1, Umn1, n);
fmpz_mod(Umn, t, n);
fmpz_clear(t);
fmpz_clear(t2);
}
/* Compute U_{km}, U_{km + 1} from U_m, U_{m + 1}, k > 0 */
void fmpz_lucas_chain_mul(fmpz_t Ukm, fmpz_t Ukm1,
const fmpz_t Um, const fmpz_t Um1,
const fmpz_t A, const fmpz_t B, const fmpz_t k,
const fmpz_t n)
{
slong i = 0, b = fmpz_sizeinbase(k, 2);
fmpz_t t, t1;
fmpz_init(t);
fmpz_init(t1);
fmpz_set(Ukm, Um);
fmpz_set(Ukm1, Um1);
while (!fmpz_tstbit(k, i))
{
fmpz_lucas_chain_double(Ukm, Ukm1, Ukm, Ukm1, A, B, n);
i++;
}
i++;
if (i < b)
{
fmpz_set(t, Ukm);
fmpz_set(t1, Ukm1);
}
while (i < b)
{
fmpz_lucas_chain_double(t, t1, t, t1, A, B, n);
if (fmpz_tstbit(k, i))
fmpz_lucas_chain_add(Ukm, Ukm1, Ukm, Ukm1, t, t1, A, B, n);
i++;
}
fmpz_clear(t);
fmpz_clear(t1);
}
/* Compute U_m, U_{m + 1} from V_m, V_{m + 1} */
void fmpz_lucas_chain_VtoU(fmpz_t Um, fmpz_t Um1,
const fmpz_t Vm, const fmpz_t Vm1,
const fmpz_t A, const fmpz_t B, const fmpz_t Dinv,
const fmpz_t n)
{
fmpz_t t;
fmpz_init(t);
fmpz_mul_2exp(t, Vm1, 1); /* (2V_{m + 1} - AV_m) / D */
fmpz_submul(t, Vm, A);
fmpz_mul(t, t, Dinv);
fmpz_set(Um1, Vm);
fmpz_mod(Um, t, n);
fmpz_addmul(Um1, Um, A); /* (V_m + AU_m) / 2 */
if (!fmpz_is_even(Um1))
fmpz_add(Um1, Um1, n);
fmpz_tdiv_q_2exp(Um1, Um1, 1);
fmpz_mod(Um1, Um1, n);
fmpz_clear(t);
}