diff --git a/helpers/prng.nim b/helpers/prng_unsafe.nim similarity index 59% rename from helpers/prng.nim rename to helpers/prng_unsafe.nim index ac4ab69..2336522 100644 --- a/helpers/prng.nim +++ b/helpers/prng_unsafe.nim @@ -14,6 +14,7 @@ import # ############################################################ # # Pseudo-Random Number Generator +# Unsafe: for testing and benchmarking purposes # # ############################################################ # @@ -29,6 +30,8 @@ import # We use 2^512 to cover the range the base field elements type RngState* = object + ## This is the state of a Xoshiro512** PRNG + ## Unsafe: for testing and benchmarking purposes only s: array[8, uint64] func splitMix64(state: var uint64): uint64 = @@ -79,8 +82,9 @@ func next(rng: var RngState): uint64 = # BigInts and Fields # ------------------------------------------------------------ -func random[T](rng: var RngState, a: var T, C: static Curve) {.noInit.}= +func random_unsafe[T](rng: var RngState, a: var T, C: static Curve) {.noInit.}= ## Recursively initialize a BigInt or Field element + ## Unsafe: for testing and benchmarking purposes only when T is BigInt: var reduced, unreduced{.noInit.}: T @@ -93,46 +97,104 @@ func random[T](rng: var RngState, a: var T, C: static Curve) {.noInit.}= else: for field in fields(a): - rng.random(field, C) + rng.random_unsafe(field, C) # Elliptic curves # ------------------------------------------------------------ -func random[F](rng: var RngState, a: var ECP_SWei_Proj[F]) = +func random_unsafe[F](rng: var RngState, a: var ECP_SWei_Proj[F]) = ## Initialize a random curve point with Z coordinate == 1 - + ## Unsafe: for testing and benchmarking purposes only var fieldElem {.noInit.}: F var success = CtFalse while not bool(success): # Euler's criterion: there are (p-1)/2 squares in a field with modulus `p` # so we have a probability of ~0.5 to get a good point - rng.random(fieldElem, F.C) + rng.random_unsafe(fieldElem, F.C) success = trySetFromCoordX(a, fieldElem) -func random_with_randZ[F](rng: var RngState, a: var ECP_SWei_Proj[F]) = +func random_unsafe_with_randZ[F](rng: var RngState, a: var ECP_SWei_Proj[F]) = ## Initialize a random curve point with Z coordinate being random - + ## Unsafe: for testing and benchmarking purposes only var Z{.noInit.}: F - rng.random(Z, F.C) # If Z is zero, X will be zero and that will be an infinity point + rng.random_unsafe(Z, F.C) # If Z is zero, X will be zero and that will be an infinity point var fieldElem {.noInit.}: F var success = CtFalse while not bool(success): - rng.random(fieldElem, F.C) + rng.random_unsafe(fieldElem, F.C) success = trySetFromCoordsXandZ(a, fieldElem, Z) +# Integer ranges +# ------------------------------------------------------------ + +func random_unsafe(rng: var RngState, maxExclusive: uint32): uint32 = + ## Generate a random integer in 0 ..< maxExclusive + ## Uses an unbiaised generation method + ## See Lemire's algorithm modified by Melissa O'Neill + ## https://www.pcg-random.org/posts/bounded-rands.html + let max = maxExclusive + var x = uint32 rng.next() + var m = x.uint64 * max.uint64 + var l = uint32 m + if l < max: + var t = not(max) + 1 # -max + if t >= max: + t -= max + if t >= max: + t = t mod max + while l < t: + x = uint32 rng.next() + m = x.uint64 * max.uint64 + l = uint32 m + return uint32(m shr 32) + # Generic over any supported type # ------------------------------------------------------------ -func random*(rng: var RngState, T: typedesc): T = - ## Create a random Field or Extension Field or Curve Element - when T is ECP_SWei_Proj: - rng.random(result) - else: - rng.random(result, T.C) +func random_unsafe*[T: SomeInteger](rng: var RngState, inclRange: Slice[T]): T = + ## Return a random integer in the given range. + ## The range bounds must fit in an int32. + let maxExclusive = inclRange.b + 1 - inclRange.a + result = T(rng.random_unsafe(uint32 maxExclusive)) + result += inclRange.a -func random_with_randZ*(rng: var RngState, T: typedesc[ECP_SWei_Proj]): T = +func random_unsafe*(rng: var RngState, T: typedesc): T = + ## Create a random Field or Extension Field or Curve Element + ## Unsafe: for testing and benchmarking purposes only + when T is ECP_SWei_Proj: + rng.random_unsafe(result) + else: + rng.random_unsafe(result, T.C) + +func random_unsafe_with_randZ*(rng: var RngState, T: typedesc[ECP_SWei_Proj]): T = ## Create a random curve element with a random Z coordinate - rng.random_with_randZ(result) + ## Unsafe: for testing and benchmarking purposes only + rng.random_unsafe_with_randZ(result) + +# Sanity checks +# ------------------------------------------------------------ + +when isMainModule: + import std/[tables, times] + + var rng: RngState + let timeSeed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 + rng.seed(timeSeed) + echo "prng_sanity_checks xoshiro512** seed: ", timeSeed + + + proc test[T](s: Slice[T]) = + var c = initCountTable[int]() + + for _ in 0 ..< 1_000_000: + c.inc(rng.random_unsafe(s)) + + echo "1'000'000 pseudo-random outputs from ", s.a, " to ", s.b, " (incl): ", c + + test(0..1) + test(0..2) + test(1..52) + test(-10..10) diff --git a/tests/test_ec_weierstrass_projective_g1.nim b/tests/test_ec_weierstrass_projective_g1.nim index 94421fd..17c1b36 100644 --- a/tests/test_ec_weierstrass_projective_g1.nim +++ b/tests/test_ec_weierstrass_projective_g1.nim @@ -8,13 +8,13 @@ import # Standard library - unittest, times, random, + unittest, times, # Internals ../constantine/config/[common, curves], ../constantine/arithmetic, ../constantine/elliptic/[ec_weierstrass_affine, ec_weierstrass_projective], # Test utilities - ../helpers/prng + ../helpers/prng_unsafe const Iters = 128 @@ -40,9 +40,9 @@ suite "Elliptic curve in Short Weierstrass form y² = x³ + a x + b with project for _ in 0 ..< Iters: var r{.noInit.}: ECP_SWei_Proj[F] when randZ: - let P = rng.random_with_randZ(ECP_SWei_Proj[F]) + let P = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) else: - let P = rng.random(ECP_SWei_Proj[F]) + let P = rng.random_unsafe(ECP_SWei_Proj[F]) r.sum(P, inf) check: bool(r == P) @@ -58,9 +58,9 @@ suite "Elliptic curve in Short Weierstrass form y² = x³ + a x + b with project for _ in 0 ..< Iters: var r{.noInit.}: ECP_SWei_Proj[F] when randZ: - let P = rng.random_with_randZ(ECP_SWei_Proj[F]) + let P = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) else: - let P = rng.random(ECP_SWei_Proj[F]) + let P = rng.random_unsafe(ECP_SWei_Proj[F]) var Q = P Q.neg() @@ -78,11 +78,11 @@ suite "Elliptic curve in Short Weierstrass form y² = x³ + a x + b with project for _ in 0 ..< Iters: var r0{.noInit.}, r1{.noInit.}: ECP_SWei_Proj[F] when randZ: - let P = rng.random_with_randZ(ECP_SWei_Proj[F]) - let Q = rng.random_with_randZ(ECP_SWei_Proj[F]) + let P = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) + let Q = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) else: - let P = rng.random(ECP_SWei_Proj[F]) - let Q = rng.random(ECP_SWei_Proj[F]) + let P = rng.random_unsafe(ECP_SWei_Proj[F]) + let Q = rng.random_unsafe(ECP_SWei_Proj[F]) r0.sum(P, Q) r1.sum(Q, P) @@ -95,13 +95,13 @@ suite "Elliptic curve in Short Weierstrass form y² = x³ + a x + b with project proc test(F: typedesc, randZ: static bool) = for _ in 0 ..< Iters: when randZ: - let a = rng.random_with_randZ(ECP_SWei_Proj[F]) - let b = rng.random_with_randZ(ECP_SWei_Proj[F]) - let c = rng.random_with_randZ(ECP_SWei_Proj[F]) + let a = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) + let b = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) + let c = rng.random_unsafe_with_randZ(ECP_SWei_Proj[F]) else: - let a = rng.random(ECP_SWei_Proj[F]) - let b = rng.random(ECP_SWei_Proj[F]) - let c = rng.random(ECP_SWei_Proj[F]) + let a = rng.random_unsafe(ECP_SWei_Proj[F]) + let b = rng.random_unsafe(ECP_SWei_Proj[F]) + let c = rng.random_unsafe(ECP_SWei_Proj[F]) var tmp1{.noInit.}, tmp2{.noInit.}: ECP_SWei_Proj[F] diff --git a/tests/test_finite_fields_mulsquare.nim b/tests/test_finite_fields_mulsquare.nim index 2a836cb..d44bf49 100644 --- a/tests/test_finite_fields_mulsquare.nim +++ b/tests/test_finite_fields_mulsquare.nim @@ -11,7 +11,7 @@ import std/unittest, std/times, ../constantine/io/[io_bigints, io_fields], ../constantine/config/[curves, common], # Test utilities - ../helpers/prng + ../helpers/prng_unsafe const Iters = 128 @@ -102,7 +102,7 @@ proc mainSelectCases() = mainSelectCases() proc randomCurve(C: static Curve) = - let a = rng.random(Fp[C]) + let a = rng.random_unsafe(Fp[C]) var r_mul, r_sqr: Fp[C] diff --git a/tests/test_finite_fields_powinv.nim b/tests/test_finite_fields_powinv.nim index 79dce00..e40d399 100644 --- a/tests/test_finite_fields_powinv.nim +++ b/tests/test_finite_fields_powinv.nim @@ -10,7 +10,7 @@ import ../constantine/arithmetic, ../constantine/io/[io_bigints, io_fields], ../constantine/config/curves, # Test utilities - ../helpers/prng, + ../helpers/prng_unsafe, # Standard library std/unittest, std/times @@ -207,7 +207,7 @@ proc main() = var aInv, r: Fp[curve] for _ in 0 ..< Iters: - let a = rng.random(Fp[curve]) + let a = rng.random_unsafe(Fp[curve]) aInv.inv(a) r.prod(a, aInv) check: bool r.isOne() diff --git a/tests/test_finite_fields_sqrt.nim b/tests/test_finite_fields_sqrt.nim index 86af23b..c6b8150 100644 --- a/tests/test_finite_fields_sqrt.nim +++ b/tests/test_finite_fields_sqrt.nim @@ -10,7 +10,7 @@ import ../constantine/[arithmetic, primitives], ../constantine/io/[io_fields], ../constantine/config/[curves, common], # Test utilities - ../helpers/prng, + ../helpers/prng_unsafe, # Standard library std/tables, std/unittest, std/times @@ -82,7 +82,7 @@ proc exhaustiveCheck_p3mod4(C: static Curve, modulus: static int) = proc randomSqrtCheck_p3mod4(C: static Curve) = test "Random square root check for p ≡ 3 (mod 4) on " & $Curve(C): for _ in 0 ..< Iters: - let a = rng.random(Fp[C]) + let a = rng.random_unsafe(Fp[C]) var na{.noInit.}: Fp[C] na.neg(a) diff --git a/tests/test_fp12.nim b/tests/test_fp12.nim index 783d44b..14559cf 100644 --- a/tests/test_fp12.nim +++ b/tests/test_fp12.nim @@ -14,7 +14,7 @@ import ../constantine/config/[common, curves], ../constantine/arithmetic, # Test utilities - ../helpers/prng + ../helpers/prng_unsafe const Iters = 128 @@ -50,12 +50,12 @@ suite "𝔽p12 = 𝔽p6[w] (irreducible polynomial w² - γ)": var accum {.noInit.}, One {.noInit.}, a{.noInit.}, na{.noInit.}, b{.noInit.}, nb{.noInit.}, a2 {.noInit.}, b2 {.noInit.}: Fp12[C] One.setOne() - a = rng.random(Fp12[C]) + a = rng.random_unsafe(Fp12[C]) a2 = a a2.double() na.neg(a) - b = rng.random(Fp12[C]) + b = rng.random_unsafe(Fp12[C]) b2.double(b) nb.neg(b) @@ -237,7 +237,7 @@ suite "𝔽p12 = 𝔽p6[w] (irreducible polynomial w² - γ)": O for _ in 0 ..< Iters: - let x {.inject.} = rng.random(Fp12[C]) + let x {.inject.} = rng.random_unsafe(Fp12[C]) var r{.noinit, inject.}: Fp12[C] body @@ -297,7 +297,7 @@ suite "𝔽p12 = 𝔽p6[w] (irreducible polynomial w² - γ)": block: proc testInstance() = for _ in 0 ..< Iters: - let a = rng.random(Fp12[C]) + let a = rng.random_unsafe(Fp12[C]) var rMul{.noInit.}, rSqr{.noInit.}: Fp12[C] rMul.prod(a, a) @@ -321,7 +321,7 @@ suite "𝔽p12 = 𝔽p6[w] (irreducible polynomial w² - γ)": block: proc testInstance() = for _ in 0 ..< Iters: - let a = rng.random(Fp12[C]) + let a = rng.random_unsafe(Fp12[C]) var na{.noInit.}: Fp12[C] na.neg(a) @@ -350,7 +350,7 @@ suite "𝔽p12 = 𝔽p6[w] (irreducible polynomial w² - γ)": for _ in 0 ..< Iters: let factor = rand(-30..30) - let a = rng.random(Fp12[C]) + let a = rng.random_unsafe(Fp12[C]) if factor == 0: continue @@ -390,9 +390,9 @@ suite "𝔽p12 = 𝔽p6[w] (irreducible polynomial w² - γ)": test "Addition is associative and commutative": proc abelianGroup(curve: static Curve) = for _ in 0 ..< Iters: - let a = rng.random(Fp12[curve]) - let b = rng.random(Fp12[curve]) - let c = rng.random(Fp12[curve]) + let a = rng.random_unsafe(Fp12[curve]) + let b = rng.random_unsafe(Fp12[curve]) + let c = rng.random_unsafe(Fp12[curve]) var tmp1{.noInit.}, tmp2{.noInit.}: Fp12[curve] @@ -441,9 +441,9 @@ suite "𝔽p12 = 𝔽p6[w] (irreducible polynomial w² - γ)": test "Multiplication is associative and commutative": proc commutativeRing(curve: static Curve) = for _ in 0 ..< Iters: - let a = rng.random(Fp12[curve]) - let b = rng.random(Fp12[curve]) - let c = rng.random(Fp12[curve]) + let a = rng.random_unsafe(Fp12[curve]) + let b = rng.random_unsafe(Fp12[curve]) + let c = rng.random_unsafe(Fp12[curve]) var tmp1{.noInit.}, tmp2{.noInit.}: Fp12[curve] @@ -502,7 +502,7 @@ suite "𝔽p12 = 𝔽p6[w] (irreducible polynomial w² - γ)": var aInv, r{.noInit.}: Fp12[curve] for _ in 0 ..< Iters: - let a = rng.random(Fp12[curve]) + let a = rng.random_unsafe(Fp12[curve]) aInv.inv(a) r.prod(a, aInv) diff --git a/tests/test_fp2.nim b/tests/test_fp2.nim index 702ac0b..a49c35c 100644 --- a/tests/test_fp2.nim +++ b/tests/test_fp2.nim @@ -14,7 +14,7 @@ import ../constantine/config/[common, curves], ../constantine/arithmetic, # Test utilities - ../helpers/prng + ../helpers/prng_unsafe const Iters = 128 @@ -74,12 +74,12 @@ suite "𝔽p2 = 𝔽p[µ] (irreducible polynomial x²+µ)": var accum {.noInit.}, One {.noInit.}, a{.noInit.}, na{.noInit.}, b{.noInit.}, nb{.noInit.}, a2 {.noInit.}, b2 {.noInit.}: Fp2[C] One.setOne() - a = rng.random(Fp2[C]) + a = rng.random_unsafe(Fp2[C]) a2 = a a2.double() na.neg(a) - b = rng.random(Fp2[C]) + b = rng.random_unsafe(Fp2[C]) b2.double(b) nb.neg(b) @@ -146,7 +146,7 @@ suite "𝔽p2 = 𝔽p[µ] (irreducible polynomial x²+µ)": O for _ in 0 ..< Iters: - let x {.inject.} = rng.random(Fp2[C]) + let x {.inject.} = rng.random_unsafe(Fp2[C]) var r{.noinit, inject.}: Fp2[C] body @@ -194,7 +194,7 @@ suite "𝔽p2 = 𝔽p[µ] (irreducible polynomial x²+µ)": block: proc testInstance() = for _ in 0 ..< Iters: - let a = rng.random(Fp2[C]) + let a = rng.random_unsafe(Fp2[C]) var rMul{.noInit.}, rSqr{.noInit.}: Fp2[C] rMul.prod(a, a) @@ -218,7 +218,7 @@ suite "𝔽p2 = 𝔽p[µ] (irreducible polynomial x²+µ)": block: proc testInstance() = for _ in 0 ..< Iters: - let a = rng.random(Fp2[C]) + let a = rng.random_unsafe(Fp2[C]) var na{.noInit.}: Fp2[C] na.neg(a) @@ -247,7 +247,7 @@ suite "𝔽p2 = 𝔽p[µ] (irreducible polynomial x²+µ)": for _ in 0 ..< Iters: let factor = rand(-30..30) - let a = rng.random(Fp2[C]) + let a = rng.random_unsafe(Fp2[C]) if factor == 0: continue @@ -287,9 +287,9 @@ suite "𝔽p2 = 𝔽p[µ] (irreducible polynomial x²+µ)": test "Addition is associative and commutative": proc abelianGroup(curve: static Curve) = for _ in 0 ..< Iters: - let a = rng.random(Fp2[curve]) - let b = rng.random(Fp2[curve]) - let c = rng.random(Fp2[curve]) + let a = rng.random_unsafe(Fp2[curve]) + let b = rng.random_unsafe(Fp2[curve]) + let c = rng.random_unsafe(Fp2[curve]) var tmp1{.noInit.}, tmp2{.noInit.}: Fp2[curve] @@ -338,9 +338,9 @@ suite "𝔽p2 = 𝔽p[µ] (irreducible polynomial x²+µ)": test "Multiplication is associative and commutative": proc commutativeRing(curve: static Curve) = for _ in 0 ..< Iters: - let a = rng.random(Fp2[curve]) - let b = rng.random(Fp2[curve]) - let c = rng.random(Fp2[curve]) + let a = rng.random_unsafe(Fp2[curve]) + let b = rng.random_unsafe(Fp2[curve]) + let c = rng.random_unsafe(Fp2[curve]) var tmp1{.noInit.}, tmp2{.noInit.}: Fp2[curve] @@ -394,7 +394,7 @@ suite "𝔽p2 = 𝔽p[µ] (irreducible polynomial x²+µ)": var aInv, r{.noInit.}: Fp2[curve] for _ in 0 ..< Iters: - let a = rng.random(Fp2[curve]) + let a = rng.random_unsafe(Fp2[curve]) aInv.inv(a) r.prod(a, aInv) check: bool(r == one) diff --git a/tests/test_fp6.nim b/tests/test_fp6.nim index 3bcf624..bde9bcf 100644 --- a/tests/test_fp6.nim +++ b/tests/test_fp6.nim @@ -14,7 +14,7 @@ import ../constantine/config/[common, curves], ../constantine/arithmetic, # Test utilities - ../helpers/prng + ../helpers/prng_unsafe const Iters = 128 @@ -50,12 +50,12 @@ suite "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³ - ξ)": var accum {.noInit.}, One {.noInit.}, a{.noInit.}, na{.noInit.}, b{.noInit.}, nb{.noInit.}, a2 {.noInit.}, b2 {.noInit.}: Fp6[C] One.setOne() - a = rng.random(Fp6[C]) + a = rng.random_unsafe(Fp6[C]) a2 = a a2.double() na.neg(a) - b = rng.random(Fp6[C]) + b = rng.random_unsafe(Fp6[C]) b2.double(b) nb.neg(b) @@ -237,7 +237,7 @@ suite "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³ - ξ)": O for _ in 0 ..< Iters: - let x {.inject.} = rng.random(Fp6[C]) + let x {.inject.} = rng.random_unsafe(Fp6[C]) var r{.noinit, inject.}: Fp6[C] body @@ -297,7 +297,7 @@ suite "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³ - ξ)": block: proc testInstance() = for _ in 0 ..< Iters: - let a = rng.random(Fp6[C]) + let a = rng.random_unsafe(Fp6[C]) var rMul{.noInit.}, rSqr{.noInit.}: Fp6[C] rMul.prod(a, a) @@ -321,7 +321,7 @@ suite "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³ - ξ)": block: proc testInstance() = for _ in 0 ..< Iters: - let a = rng.random(Fp6[C]) + let a = rng.random_unsafe(Fp6[C]) var na{.noInit.}: Fp6[C] na.neg(a) @@ -350,7 +350,7 @@ suite "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³ - ξ)": for _ in 0 ..< Iters: let factor = rand(-30..30) - let a = rng.random(Fp6[C]) + let a = rng.random_unsafe(Fp6[C]) if factor == 0: continue @@ -390,9 +390,9 @@ suite "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³ - ξ)": test "Addition is associative and commutative": proc abelianGroup(curve: static Curve) = for _ in 0 ..< Iters: - let a = rng.random(Fp6[curve]) - let b = rng.random(Fp6[curve]) - let c = rng.random(Fp6[curve]) + let a = rng.random_unsafe(Fp6[curve]) + let b = rng.random_unsafe(Fp6[curve]) + let c = rng.random_unsafe(Fp6[curve]) var tmp1{.noInit.}, tmp2{.noInit.}: Fp6[curve] @@ -441,9 +441,9 @@ suite "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³ - ξ)": test "Multiplication is associative and commutative": proc commutativeRing(curve: static Curve) = for _ in 0 ..< Iters: - let a = rng.random(Fp6[curve]) - let b = rng.random(Fp6[curve]) - let c = rng.random(Fp6[curve]) + let a = rng.random_unsafe(Fp6[curve]) + let b = rng.random_unsafe(Fp6[curve]) + let c = rng.random_unsafe(Fp6[curve]) var tmp1{.noInit.}, tmp2{.noInit.}: Fp6[curve] @@ -502,7 +502,7 @@ suite "𝔽p6 = 𝔽p2[v] (irreducible polynomial v³ - ξ)": var aInv, r{.noInit.}: Fp6[curve] for _ in 0 ..< Iters: - let a = rng.random(Fp6[curve]) + let a = rng.random_unsafe(Fp6[curve]) aInv.inv(a) r.prod(a, aInv)