2020-02-09 21:01:01 +00:00
# Constantine
# Copyright (c) 2018-2019 Status Research & Development GmbH
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import
# Standard library
2020-06-14 13:39:06 +00:00
std / [ random , macros , times , strutils ] ,
2020-02-09 21:01:01 +00:00
# Third-party
gmp , stew / byteutils ,
# Internal
2022-02-27 00:49:08 +00:00
.. / .. / constantine / math / io / io_bigints ,
.. / .. / constantine / math / arithmetic ,
2022-09-15 07:33:34 +00:00
.. / .. / constantine / platforms / abstractions ,
# Test utilities
.. / .. / helpers / prng_unsafe
2020-02-09 21:01:01 +00:00
2020-06-15 20:58:56 +00:00
echo " \n ------------------------------------------------------ \n "
2020-02-09 21:01:01 +00:00
# We test up to 1024-bit, more is really slow
var bitSizeRNG {. compileTime . } = initRand ( 1234 )
2020-06-14 13:39:06 +00:00
macro testRandomModSizes ( numSizes : static int , rBits , aBits , bBits , body : untyped ) : untyped =
2020-02-09 21:01:01 +00:00
## Generate `numSizes` random bit sizes known at compile-time to test against GMP
## for A mod M
result = newStmtList ( )
for _ in 0 .. < numSizes :
2020-06-14 13:39:06 +00:00
let aBitsVal = bitSizeRNG . rand ( 126 .. 2048 )
let bBitsVal = bitSizeRNG . rand ( 126 .. 2048 )
let rBitsVal = bitSizeRNG . rand ( 62 .. 4096 + 128 )
2020-02-09 21:01:01 +00:00
result . add quote do :
block :
const ` aBits ` = ` aBitsVal `
2020-06-14 13:39:06 +00:00
const ` bBits ` = ` bBitsVal `
const ` rBits ` = ` rBitsVal `
2020-02-09 21:01:01 +00:00
block :
` body `
const # https://gmplib.org/manual/Integer-Import-and-Export.html
2020-06-14 13:39:06 +00:00
GMP_WordLittleEndian {. used . } = - 1 'i32
GMP_WordNativeEndian {. used . } = 0 'i32
GMP_WordBigEndian {. used . } = 1 'i32
2020-02-09 21:01:01 +00:00
GMP_MostSignificantWordFirst = 1 'i32
2020-06-14 13:39:06 +00:00
GMP_LeastSignificantWordFirst {. used . } = - 1 'i32
2020-02-09 21:01:01 +00:00
proc main ( ) =
2022-09-15 07:33:34 +00:00
var rng : RngState
2020-02-09 21:01:01 +00:00
let seed = uint32 ( getTime ( ) . toUnix ( ) and ( 1 'i64 shl 32 - 1 ) ) # unixTime mod 2^32
2022-09-15 07:33:34 +00:00
rng . seed ( seed )
echo " \n ------------------------------------------------------ \n "
echo " test_bigints_mod_vs_gmp xoshiro512** seed: " , seed
2020-02-09 21:01:01 +00:00
2020-06-14 13:39:06 +00:00
var r , a , b : mpz_t
2020-02-09 21:01:01 +00:00
mpz_init ( r )
2020-06-14 13:39:06 +00:00
mpz_init ( a )
mpz_init ( b )
2020-02-09 21:01:01 +00:00
2020-09-21 21:24:00 +00:00
testRandomModSizes ( 12 , rBits , aBits , bBits ) :
2020-02-10 22:56:57 +00:00
# echo "--------------------------------------------------------------------------------"
2020-06-14 13:39:06 +00:00
echo " Testing: random mul r ( " , align ( $ rBits , 4 ) , " -bit) <- a ( " , align ( $ aBits , 4 ) , " -bit) * b ( " , align ( $ bBits , 4 ) , " -bit) (full mul bits: " , align ( $ ( aBits + bBits ) , 4 ) , " ), r large enough? " , rBits > = aBits + bBits
2020-02-09 21:01:01 +00:00
2022-09-15 07:33:34 +00:00
# Build the bigints
let aTest = rng . random_unsafe ( BigInt [ aBits ] )
var bTest = rng . random_unsafe ( BigInt [ bBits ] )
2020-02-09 22:26:39 +00:00
2020-02-09 21:01:01 +00:00
#########################################################
2022-09-15 07:33:34 +00:00
# Conversion to GMP
2020-02-09 21:01:01 +00:00
const aLen = ( aBits + 7 ) div 8
2020-06-14 13:39:06 +00:00
const bLen = ( bBits + 7 ) div 8
2020-02-09 21:01:01 +00:00
var aBuf : array [ aLen , byte ]
2020-06-14 13:39:06 +00:00
var bBuf : array [ bLen , byte ]
2020-02-09 21:01:01 +00:00
2022-09-15 07:33:34 +00:00
aBuf . marshal ( aTest , bigEndian )
bBuf . marshal ( bTest , bigEndian )
2020-02-09 21:01:01 +00:00
2022-09-15 07:33:34 +00:00
mpz_import ( a , aLen , GMP_MostSignificantWordFirst , 1 , GMP_WordNativeEndian , 0 , aBuf [ 0 ] . addr )
mpz_import ( b , bLen , GMP_MostSignificantWordFirst , 1 , GMP_WordNativeEndian , 0 , bBuf [ 0 ] . addr )
2020-02-09 21:01:01 +00:00
#########################################################
2020-06-14 13:39:06 +00:00
# Multiplication
mpz_mul ( r , a , b )
2020-02-09 21:01:01 +00:00
2020-06-14 13:39:06 +00:00
# If a*b overflow the result size we truncate
const numWords = wordsRequired ( rBits )
when numWords < wordsRequired ( aBits + bBits ) :
echo " truncating from " , wordsRequired ( aBits + bBits ) , " words to " , numWords , " (2^ " , WordBitwidth * numWords , " ) "
r . mpz_tdiv_r_2exp ( r , WordBitwidth * numWords )
# Constantine
var rTest : BigInt [ rBits ]
rTest . prod ( aTest , bTest )
2020-02-09 21:01:01 +00:00
#########################################################
# Check
2022-09-15 07:33:34 +00:00
2020-06-14 13:39:06 +00:00
{. push warnings : off . } # deprecated csize
2022-09-15 07:33:34 +00:00
var aW , bW , rW : csize # Word written by GMP
2020-06-14 13:39:06 +00:00
{. pop . }
2022-09-15 07:33:34 +00:00
const rLen = numWords * WordBitWidth
var rGMP : array [ rLen , byte ]
2020-03-16 15:33:51 +00:00
discard mpz_export ( rGMP [ 0 ] . addr , rW . addr , GMP_MostSignificantWordFirst , 1 , GMP_WordNativeEndian , 0 , r )
2020-02-09 21:01:01 +00:00
2020-06-14 13:39:06 +00:00
var rConstantine : array [ rLen , byte ]
2022-02-28 08:23:26 +00:00
marshal ( rConstantine , rTest , bigEndian )
2020-02-09 21:01:01 +00:00
2020-03-16 15:33:51 +00:00
# Note: in bigEndian, GMP aligns left while constantine aligns right
2020-06-14 13:39:06 +00:00
doAssert rGMP . toOpenArray ( 0 , rW - 1 ) = = rConstantine . toOpenArray ( rLen - rW , rLen - 1 ) , block :
2020-02-10 22:56:57 +00:00
# Reexport as bigEndian for debugging
discard mpz_export ( aBuf [ 0 ] . addr , aW . addr , GMP_MostSignificantWordFirst , 1 , GMP_WordNativeEndian , 0 , a )
2020-06-14 13:39:06 +00:00
discard mpz_export ( bBuf [ 0 ] . addr , bW . addr , GMP_MostSignificantWordFirst , 1 , GMP_WordNativeEndian , 0 , b )
" \n Multiplication with operands \n " &
2020-02-10 22:56:57 +00:00
" a ( " & align ( $ aBits , 4 ) & " -bit): " & aBuf . toHex & " \n " &
2020-06-14 13:39:06 +00:00
" b ( " & align ( $ bBits , 4 ) & " -bit): " & bBuf . toHex & " \n " &
" into r of size " & align ( $ rBits , 4 ) & " -bit failed: " & " \n " &
2020-02-10 22:56:57 +00:00
" GMP: " & rGMP . toHex ( ) & " \n " &
2020-03-16 15:33:51 +00:00
" Constantine: " & rConstantine . toHex ( ) & " \n " &
" (Note that GMP aligns bytes left while constantine aligns bytes right) "
2020-02-09 21:01:01 +00:00
main ( )