//***********************************************************************
		// Performs the calculation of the kth term in the Lucas Sequence.
		// For details of the algorithm, see reference [9].
		//
		// k must be odd.  i.e LSB == 1
		//***********************************************************************

		private static BigInteger[] LucasSequenceHelper(BigInteger P, BigInteger Q,
														BigInteger k, BigInteger n,
														BigInteger constant, int s)
		{
			unchecked
			{
				BigInteger[] result = new BigInteger[3];

				if ((k.data[0] & 0x00000001) == 0)
					throw (new ArgumentException("Argument k must be odd."));

				int numbits = k.BitCount();
				uint mask = (uint)0x1 << ((numbits & 0x1F) - 1);

				// v = v0, v1 = v1, u1 = u1, Q_k = Q^0

				BigInteger v = 2 % n,
					Q_k = 1 % n,
					v1 = P % n,
					u1 = Q_k;
				bool flag = true;

				for (int i = k.dataLength - 1; i >= 0; i--) // iterate on the binary expansion of k
				{
					//Console.WriteLine("round");
					while (mask != 0)
					{
						if (i == 0 && mask == 0x00000001) // last bit
							break;

						if ((k.data[i] & mask) != 0) // bit is set
						{
							// index doubling with addition

							u1 = (u1 * v1) % n;

							v = ((v * v1) - (P * Q_k)) % n;
							v1 = BarrettReduction(v1 * v1, n, constant);
							v1 = (v1 - ((Q_k * Q) << 1)) % n;

							if (flag)
								flag = false;
							else
								Q_k = BarrettReduction(Q_k * Q_k, n, constant);

							Q_k = (Q_k * Q) % n;
						}
						else
						{
							// index doubling
							u1 = ((u1 * v) - Q_k) % n;

							v1 = ((v * v1) - (P * Q_k)) % n;
							v = BarrettReduction(v * v, n, constant);
							v = (v - (Q_k << 1)) % n;

							if (flag)
							{
								Q_k = Q % n;
								flag = false;
							}
							else
								Q_k = BarrettReduction(Q_k * Q_k, n, constant);
						}

						mask >>= 1;
					}
					mask = 0x80000000;
				}

				// at this point u1 = u(n+1) and v = v(n)
				// since the last bit always 1, we need to transform u1 to u(2n+1) and v to v(2n+1)

				u1 = ((u1 * v) - Q_k) % n;
				v = ((v * v1) - (P * Q_k)) % n;
				if (flag)
					flag = false;
				else
					Q_k = BarrettReduction(Q_k * Q_k, n, constant);

				Q_k = (Q_k * Q) % n;

				for (int i = 0; i < s; i++)
				{
					// index doubling
					u1 = (u1 * v) % n;
					v = ((v * v) - (Q_k << 1)) % n;

					if (flag)
					{
						Q_k = Q % n;
						flag = false;
					}
					else
						Q_k = BarrettReduction(Q_k * Q_k, n, constant);
				}

				result[0] = u1;
				result[1] = v;
				result[2] = Q_k;

				return result;
			}
		}
		//***********************************************************************
		// Modulo Exponentiation
		//***********************************************************************

		/// <summary>
		/// Modulo Exponentiation
		/// </summary>
		/// <param name="exponent">The exponent.</param>
		/// <param name="modulus">The modulus.</param>
		/// <returns></returns>
		public BigInteger ModPow(BigInteger exponent, BigInteger modulus)
		{
			unchecked
			{
				if (exponent == null)
					throw new ArgumentNullException("exponent");
				if (modulus == null)
					throw new ArgumentNullException("modulus");
				if ((exponent.data[maxLength - 1] & 0x80000000) != 0)
					throw (new ArithmeticException("Positive exponents only."));

				BigInteger resultNum = 1;
				BigInteger tempNum;
				bool thisNegative = false;

				if ((data[maxLength - 1] & 0x80000000) != 0) // negative this
				{
					tempNum = -this % modulus;
					thisNegative = true;
				}
				else
					tempNum = this % modulus; // ensures (tempNum * tempNum) < b^(2k)

				if ((modulus.data[maxLength - 1] & 0x80000000) != 0) // negative n
					modulus = -modulus;

				// calculate constant = b^(2k) / m
				BigInteger constant = new BigInteger();

				int i = modulus.dataLength << 1;
				constant.data[i] = 0x00000001;
				constant.dataLength = i + 1;

				constant = constant / modulus;
				int totalBits = exponent.BitCount();
				int count = 0;

				// perform squaring and multiply exponentiation
				for (int pos = 0; pos < exponent.dataLength; pos++)
				{
					uint mask = 0x01;
					//Console.WriteLine("pos = " + pos);

					for (int index = 0; index < 32; index++)
					{
						if ((exponent.data[pos] & mask) != 0)
							resultNum = BarrettReduction(resultNum * tempNum, modulus, constant);

						mask <<= 1;

						tempNum = BarrettReduction(tempNum * tempNum, modulus, constant);

						if (tempNum.dataLength == 1 && tempNum.data[0] == 1)
						{
							if (thisNegative && (exponent.data[0] & 0x1) != 0) //odd exp
								return -resultNum;
							return resultNum;
						}
						count++;
						if (count == totalBits)
							break;
					}
				}

				if (thisNegative && (exponent.data[0] & 0x1) != 0) //odd exp
					return -resultNum;

				return resultNum;
			}
		}