private static BigInteger[] fastLucasSequence(
BigInteger p,
BigInteger P,
BigInteger Q,
BigInteger k)
{
// TODO Research and apply "common-multiplicand multiplication here"
int n = k.BitLength;
int s = k.GetLowestSetBit();
Debug.Assert(k.TestBit(s));
BigInteger Uh = BigInteger.One;
BigInteger Vl = BigInteger.Two;
BigInteger Vh = P;
BigInteger Ql = BigInteger.One;
BigInteger Qh = BigInteger.One;
for (int j = n - 1; j >= s + 1; --j)
{
Ql = Ql.Multiply(Qh).Mod(p);
if (k.TestBit(j))
{
Qh = Ql.Multiply(Q).Mod(p);
Uh = Uh.Multiply(Vh).Mod(p);
Vl = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p);
Vh = Vh.Multiply(Vh).Subtract(Qh.ShiftLeft(1)).Mod(p);
}
else
{
Qh = Ql;
Uh = Uh.Multiply(Vl).Subtract(Ql).Mod(p);
Vh = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p);
Vl = Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)).Mod(p);
}
}
Ql = Ql.Multiply(Qh).Mod(p);
Qh = Ql.Multiply(Q).Mod(p);
Uh = Uh.Multiply(Vl).Subtract(Ql).Mod(p);
Vl = Vh.Multiply(Vl).Subtract(P.Multiply(Ql)).Mod(p);
Ql = Ql.Multiply(Qh).Mod(p);
for (int j = 1; j <= s; ++j)
{
Uh = Uh.Multiply(Vl).Mod(p);
Vl = Vl.Multiply(Vl).Subtract(Ql.ShiftLeft(1)).Mod(p);
Ql = Ql.Multiply(Ql).Mod(p);
}
return new BigInteger[]{ Uh, Vl };
}