From 949fef48fd465bca952a3f97dda99e5a1b12ea47 Mon Sep 17 00:00:00 2001 From: keymoon Date: Wed, 9 Jun 2021 16:09:58 +0900 Subject: [PATCH 1/9] fix formula in comment --- .../Numerics/BigIntegerCalculator.FastReducer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 8731794b40299..504f146af8a8b 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -27,11 +27,11 @@ public FastReducer(uint[] modulus) { Debug.Assert(modulus != null); - // Let r = 4^k, with 2^k > m + // Let r = (2^32)^(2k), with (2^32)^k > m uint[] r = new uint[modulus.Length * 2 + 1]; r[r.Length - 1] = 1; - // Let mu = 4^k / m + // Let mu = r / m _mu = Divide(r, modulus); _modulus = modulus; @@ -52,15 +52,15 @@ public int Reduce(uint[] value, int length) if (length < _modulus.Length) return length; - // Let q1 = v/2^(k-1) * mu + // Let q1 = v/(2^32)^(k-1) * mu int l1 = DivMul(value, length, _mu, _muLength, _q1, _modulus.Length - 1); - // Let q2 = q1/2^(k+1) * m + // Let q2 = q1/(2^32)^(k+1) * m int l2 = DivMul(_q1, l1, _modulus, _modulus.Length, _q2, _modulus.Length + 1); - // Let v = (v - q2) % 2^(k+1) - i*m + // Let v = (v - q2) % (2^32)^(k+1) - i*m return SubMod(value, length, _q2, l2, _modulus, _modulus.Length + 1); } From 6d3345a908f6b69c94a28fe8daea63a32ea90e87 Mon Sep 17 00:00:00 2001 From: keymoon Date: Thu, 10 Jun 2021 02:13:58 +0900 Subject: [PATCH 2/9] add test for FastReducer boundary case --- .../tests/BigInteger/modpow.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/modpow.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/modpow.cs index 54af681215170..56159983edcf8 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/modpow.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/modpow.cs @@ -286,6 +286,36 @@ public static void ModPowBoundary() VerifyModPowString(Math.Pow(2, 35) + " " + Math.Pow(2, 33) + " 2 tModPow"); } + [Fact] + [OuterLoop] + public static void ModPowFastReducerBoundary() + { + BigIntTools.Utils.RunWithFakeThreshold("ReducerThreshold", 8, () => + { + byte[] tempByteArray1 = new byte[40]; + byte[] tempByteArray2 = new byte[40]; + byte[] tempByteArray3 = new byte[40]; + + for (int i = 0; i < 32; i++) + { + tempByteArray2[i] = 0xff; + } + tempByteArray3[36] = 1; + + for (int i = 32; i < 40; i++) + { + for (int j = 0; j < 8; j++) + { + tempByteArray1[i] = (byte)(1 << j); + tempByteArray2[i] |= (byte)(1 << j); + VerifyModPowString(Print(tempByteArray3) + "2 " + Print(tempByteArray2) + "tModPow"); + VerifyModPowString(Print(tempByteArray3) + "2 " + Print(tempByteArray1) + "tModPow"); + } + tempByteArray1[i] = 0; + } + }); + } + private static void VerifyModPowString(string opstring) { StackCalc sc = new StackCalc(opstring); From 9c967ba29059fb071e77e699e8865df8f1925fb1 Mon Sep 17 00:00:00 2001 From: keymoon Date: Thu, 10 Jun 2021 02:15:44 +0900 Subject: [PATCH 3/9] fix size of buffer used in FastReducer --- .../System/Numerics/BigIntegerCalculator.FastReducer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 504f146af8a8b..620e681e895d4 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -35,11 +35,11 @@ public FastReducer(uint[] modulus) _mu = Divide(r, modulus); _modulus = modulus; - // Allocate memory for quotients once - _q1 = new uint[modulus.Length * 2 + 2]; - _q2 = new uint[modulus.Length * 2 + 1]; - _muLength = ActualLength(_mu); + + // Allocate memory for quotients once + _q1 = new uint[_muLength + modulus.Length + 1]; + _q2 = new uint[_muLength + modulus.Length]; } public int Reduce(uint[] value, int length) From c8d064d872d7dd51a644105645f74894936be271 Mon Sep 17 00:00:00 2001 From: keymoon Date: Mon, 21 Jun 2021 01:08:03 +0900 Subject: [PATCH 4/9] Add testcase to boundary test --- .../tests/BigInteger/modpow.cs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/modpow.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/modpow.cs index 56159983edcf8..654204bdeffdd 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/modpow.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/modpow.cs @@ -295,12 +295,22 @@ public static void ModPowFastReducerBoundary() byte[] tempByteArray1 = new byte[40]; byte[] tempByteArray2 = new byte[40]; byte[] tempByteArray3 = new byte[40]; + byte[] tempByteArray4 = new byte[40]; + byte[] tempByteArray5 = new byte[40]; + byte[] tempByteArray6 = new byte[40]; for (int i = 0; i < 32; i++) { tempByteArray2[i] = 0xff; } - tempByteArray3[36] = 1; + tempByteArray3[0] = 1; + for (int i = 0; i < 36; i++) + { + tempByteArray4[i] = 0xff; + } + tempByteArray5[36] = 1; + tempByteArray6[0] = 1; + tempByteArray6[36] = 1; for (int i = 32; i < 40; i++) { @@ -308,10 +318,19 @@ public static void ModPowFastReducerBoundary() { tempByteArray1[i] = (byte)(1 << j); tempByteArray2[i] |= (byte)(1 << j); - VerifyModPowString(Print(tempByteArray3) + "2 " + Print(tempByteArray2) + "tModPow"); - VerifyModPowString(Print(tempByteArray3) + "2 " + Print(tempByteArray1) + "tModPow"); + tempByteArray3[i] = (byte)(1 << j); + VerifyModPowString(Print(tempByteArray4) + "2 " + Print(tempByteArray1) + "tModPow"); + VerifyModPowString(Print(tempByteArray5) + "2 " + Print(tempByteArray1) + "tModPow"); + VerifyModPowString(Print(tempByteArray6) + "2 " + Print(tempByteArray1) + "tModPow"); + VerifyModPowString(Print(tempByteArray4) + "2 " + Print(tempByteArray2) + "tModPow"); + VerifyModPowString(Print(tempByteArray5) + "2 " + Print(tempByteArray2) + "tModPow"); + VerifyModPowString(Print(tempByteArray6) + "2 " + Print(tempByteArray2) + "tModPow"); + VerifyModPowString(Print(tempByteArray4) + "2 " + Print(tempByteArray3) + "tModPow"); + VerifyModPowString(Print(tempByteArray5) + "2 " + Print(tempByteArray3) + "tModPow"); + VerifyModPowString(Print(tempByteArray6) + "2 " + Print(tempByteArray3) + "tModPow"); } tempByteArray1[i] = 0; + tempByteArray3[i] = 0; } }); } From 5626f23ae05bc26771c758590d64cdb50bdd64a4 Mon Sep 17 00:00:00 2001 From: keymoon Date: Mon, 21 Jun 2021 02:59:18 +0900 Subject: [PATCH 5/9] fix assertion error in SubMod function --- .../BigIntegerCalculator.FastReducer.cs | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 620e681e895d4..ec0f6083bd5f3 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -130,7 +130,7 @@ private static unsafe int SubMod(uint[] left, int leftLength, fixed (uint* l = left, r = right, m = modulus) { - SubtractSelf(l, leftLength, r, rightLength); + OverFlowableSubtractSelf(l, leftLength, r, rightLength); leftLength = ActualLength(left, leftLength); while (Compare(l, leftLength, m, modulus.Length) >= 0) @@ -144,6 +144,34 @@ private static unsafe int SubMod(uint[] left, int leftLength, return leftLength; } + + private static unsafe void OverFlowableSubtractSelf(uint* left, int leftLength, + uint* right, int rightLength) + { + Debug.Assert(leftLength >= 0); + Debug.Assert(rightLength >= 0); + Debug.Assert(leftLength >= rightLength); + + // Executes the "grammar-school" algorithm for computing z = a - b. + // Same as above, but we're writing the result directly to a and + // stop execution, if we're out of b and c is already 0. + + int i = 0; + long carry = 0L; + + for (; i < rightLength; i++) + { + long digit = (left[i] + carry) - right[i]; + left[i] = unchecked((uint)digit); + carry = digit >> 32; + } + for (; carry != 0 && i < leftLength; i++) + { + long digit = left[i] + carry; + left[i] = (uint)digit; + carry = digit >> 32; + } + } } } } From 05b78c1977088a12af8d6e5d1e0dffe2682f553b Mon Sep 17 00:00:00 2001 From: keymoon Date: Mon, 21 Jun 2021 03:00:06 +0900 Subject: [PATCH 6/9] reduce k from k + 1 --- .../src/System/Numerics/BigIntegerCalculator.FastReducer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index ec0f6083bd5f3..701f0c4b28423 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -60,9 +60,10 @@ public int Reduce(uint[] value, int length) int l2 = DivMul(_q1, l1, _modulus, _modulus.Length, _q2, _modulus.Length + 1); - // Let v = (v - q2) % (2^32)^(k+1) - i*m + // Let v = (v - q2) % (2^32)^k + // while m <= v: Let v = v - m return SubMod(value, length, _q2, l2, - _modulus, _modulus.Length + 1); + _modulus, _modulus.Length); } private static unsafe int DivMul(uint[] left, int leftLength, From a66b57123701e9c17ae637fa462eeeb2f1fa6c7f Mon Sep 17 00:00:00 2001 From: keymoon Date: Thu, 7 Oct 2021 15:49:19 +0900 Subject: [PATCH 7/9] Fix function name and description --- .../System/Numerics/BigIntegerCalculator.FastReducer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 701f0c4b28423..856a2ecaad466 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -131,7 +131,7 @@ private static unsafe int SubMod(uint[] left, int leftLength, fixed (uint* l = left, r = right, m = modulus) { - OverFlowableSubtractSelf(l, leftLength, r, rightLength); + OverflowableSubtractSelf(l, leftLength, r, rightLength); leftLength = ActualLength(left, leftLength); while (Compare(l, leftLength, m, modulus.Length) >= 0) @@ -146,7 +146,7 @@ private static unsafe int SubMod(uint[] left, int leftLength, return leftLength; } - private static unsafe void OverFlowableSubtractSelf(uint* left, int leftLength, + private static unsafe void OverflowableSubtractSelf(uint* left, int leftLength, uint* right, int rightLength) { Debug.Assert(leftLength >= 0); @@ -154,8 +154,8 @@ private static unsafe void OverFlowableSubtractSelf(uint* left, int leftLength, Debug.Assert(leftLength >= rightLength); // Executes the "grammar-school" algorithm for computing z = a - b. - // Same as above, but we're writing the result directly to a and - // stop execution, if we're out of b and c is already 0. + // We're writing the result directly to a and + // stop execution, if we're out of b. int i = 0; long carry = 0L; From bdc5196c043d7e2659acd4b9eded1880239470cf Mon Sep 17 00:00:00 2001 From: keymoon Date: Mon, 29 Nov 2021 13:41:30 +0900 Subject: [PATCH 8/9] Add assertion for SubMod --- .../src/System/Numerics/BigIntegerCalculator.FastReducer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 1edd6c8836051..3575c9aafc212 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -91,6 +91,8 @@ private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Spa private static int SubMod(Span left, ReadOnlySpan right, ReadOnlySpan modulus, int k) { + Debug.Assert(left.Length >= k); + // Executes the subtraction algorithm for left and right, // but considers only the first k limbs, which is equivalent to // preceding reduction by 2^(32*k). Furthermore, if left is From a53f36e8795ca5eaace6ea757141415ee8ff377d Mon Sep 17 00:00:00 2001 From: keymoon Date: Mon, 29 Nov 2021 13:51:56 +0900 Subject: [PATCH 9/9] fix scale of k --- .../BigIntegerCalculator.FastReducer.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs index 3575c9aafc212..d35f2af339932 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigIntegerCalculator.FastReducer.cs @@ -38,15 +38,15 @@ public int Reduce(Span value) if (value.Length < _modulus.Length) return value.Length; - // Let q1 = v/(2^32)^(k-1) * mu + // Let q1 = v/2^(k-32) * mu _q1.Clear(); int l1 = DivMul(value, _mu, _q1, _modulus.Length - 1); - // Let q2 = q1/(2^32)^(k+1) * m + // Let q2 = q1/2^(k+32) * m _q2.Clear(); int l2 = DivMul(_q1.Slice(0, l1), _modulus, _q2, _modulus.Length + 1); - // Let v = (v - q2) % (2^32)^k + // Let v = (v - q2) % 2^k // while m <= v: Let v = v - m var length = SubMod(value, _q2.Slice(0, l2), _modulus, _modulus.Length); value = value.Slice(length); @@ -65,6 +65,10 @@ private static int DivMul(ReadOnlySpan left, ReadOnlySpan right, Spa // but skips the first k limbs of left, which is equivalent to // preceding division by 2^(32*k). To spare memory allocations // we write the result to an already allocated memory. + // Note that the k used here has different scale from the k used + // in the description of barrett reduction. + // The former refers to the number of elements in the array, + // while the latter refers to the number of bits. if (left.Length > k) { @@ -97,6 +101,10 @@ private static int SubMod(Span left, ReadOnlySpan right, ReadOnlySpa // but considers only the first k limbs, which is equivalent to // preceding reduction by 2^(32*k). Furthermore, if left is // still greater than modulus, further subtractions are used. + // Note that the k used here has different scale from the k used + // in the description of barrett reduction. + // The former refers to the number of elements in the array, + // while the latter refers to the number of bits. if (left.Length > k) left = left.Slice(0, k); @@ -162,7 +170,7 @@ public FastReducerConstructorHelper(ReadOnlySpan modulus, Span r, Sp Debug.Assert(r.Length == modulus.Length * 2 + 1); Debug.Assert(mu.Length == r.Length - modulus.Length + 1); - // Let r = (2^32)^(2k), with (2^32)^k > m + // Let r = 2^(2k), with 2^k > m and k % 32 = 0 r[r.Length - 1] = 1; // Let mu = r / m @@ -188,6 +196,5 @@ public void AddQs(Span q1, Span q2) Q2 = q2; } } - } }