public IntegerNumber ModPow(IntegerNumber power, IntegerNumber prime, Func <IntegerNumber, IntegerNumber> modPFunction) { IntegerNumber a = this; if (power.IsNegative) { a = a.ModInverse(prime); power = -power; } int highestBitPosition = power.Digits * 64 - 1; while (highestBitPosition >= 0 && !power.GetBitAt(highestBitPosition)) { highestBitPosition--; } IntegerNumber result = highestBitPosition < 0 ? One : a; for (int i = highestBitPosition; --i >= 0;) { result = modPFunction(result.Square()); if (power.GetBitAt(i)) { result = modPFunction(result * a); } } var diff = result - prime; return(diff.IsNegative ? result : diff); }
//f[x_] := x^2 - y //FullSimplify[x - f[x] / D[f[x], x]] = (x + y / x) / 2 [Newton method] //FullSimplify[x - 2*f[x]*D[f[x], x]/(2*D[f[x], x]^2 - f[x]*D[f[x], {x, 2}])] = x * (x^2 + 3 y) / (3 x^2 + y) [Halley] //In practice Newton method is faster than Halley 3'rd order convergence for SQRT. public IntegerNumber Sqrt() { if (this.IsNegative) { throw new ArithmeticException("NaN"); } if (this.IsZero) { return(IntegerNumber.Zero); } shrink(this); int n = this.Digits; int computedDigits = (n & 1) != 0 ? 1 : 2; if (computedDigits + 2 <= n) { computedDigits += 2; } ulong[] inputBits = this.bits; IntegerNumber t = this.GetDigits(n - computedDigits, computedDigits, true); double estimate = Math.Sqrt(t.ToDouble); double highDouble = Math.Floor(estimate / Power2_64); double lowDouble = estimate - highDouble * Power2_64; IntegerNumber root = new IntegerNumber(new ulong[] { (ulong)lowDouble, (ulong)highDouble }, false); if (root.IsNegative) { Array.Resize(ref root.bits, 3); } while (true) { IntegerNumber oldRoot = root; root = (root + t / root) >> 1; if (IntegerNumber.Abs(oldRoot - root) <= IntegerNumber.One) { break; } } int additionalDigits = 1; for (int i = computedDigits; i < n + additionalDigits;) { int digits = Math.Min(computedDigits, n - computedDigits + 1) >> 1; i += computedDigits; root <<= digits * 64; computedDigits += digits * 2; t = computedDigits >= n ? this : this.GetDigits(n - computedDigits, computedDigits, true); root = (root + t / root) >> 1; } IntegerNumber square = root.Square(); return(this >= square ? root : root - IntegerNumber.One); }
public static IntegerNumber Pow(int baseNumber, int exponent) { int highestBitPosition = BSR((uint)exponent); if (highestBitPosition < 0) { return(IntegerNumber.One); } IntegerNumber number = new IntegerNumber(baseNumber); IntegerNumber result = number; for (int i = highestBitPosition; --i >= 0;) { result = result.Square(); shrink(result); if ((exponent & (1 << i)) != 0) { result *= baseNumber; } } return(result); }
//InverseSqrt[this] = Sqrt[(this << (this.Digits * 64)).Inverse()] //InverseSqrt[this] = Sqrt[(1 << (this.Digits * 192)) / this] //f[x_] := (1/x)^2 - y //FullSimplify[x - f[x] / D[f[x], x]] = x * (1/2) * (3 - x^2 y) public IntegerNumber InverseSqrt() { if (this.IsNegative || this.IsZero) { throw new ArithmeticException("NaN"); } shrink(this); int n = this.Digits; int computedDigits = (n & 1) != 0 ? 1 : 2; if (computedDigits + 2 <= n) { computedDigits += 2; } ulong[] inputBits = this.bits; IntegerNumber t = this.GetDigits(n - computedDigits, computedDigits, true); double estimate = Math.Pow(Power2_64, computedDigits * 1.5) / Math.Sqrt(t.ToDouble); List <ulong> initialDigits = new List <ulong>(); while (estimate != 0) { initialDigits.Add((ulong)Math.IEEERemainder(estimate, Power2_64)); estimate = Math.Floor(estimate / Power2_64); } IntegerNumber root = new IntegerNumber(initialDigits.ToArray(), false); IntegerNumber three = new IntegerNumber(3L); //repeat iteration once to reach full precision. int iterations1 = (int)Math.Ceiling(Math.Log(initialDigits.Count * 64.0 / 52, 2.0)); for (int step = iterations1; --step >= 0;) { int shift = computedDigits * 64 * 3; root = root * ((three << shift) - root.Square() * t) >> (1 + shift); shrink(root); } if (computedDigits == n) { return(root); } int additionalDigits = 2; int totalDigits = n + additionalDigits; do { int digits = Math.Min(Math.Max(computedDigits - 1, 1), totalDigits - computedDigits); computedDigits += digits; t = this.GetDigits(n - computedDigits, computedDigits, true); //root <<= digits * 64; //int shift = computedDigits * 3 * 64; //root = root * ((three << shift) - root.Square() * t) >> (1 + shift); int shift1 = (computedDigits * 3 - digits * 2) * 64; int shift2 = (computedDigits * 3 - digits * 3) * 64; root = root * ((three << shift1) - root.Square() * t) >> (1 + shift2); shrink(root); } while (computedDigits < totalDigits); root >>= additionalDigits * 64; shrink(root); return(root); }