From 130a87ef431d7339cd0de205d27dd2df847f74b1 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Sun, 30 Dec 2018 17:14:11 -0800 Subject: [PATCH] Add support for signed modular inversion. --- src/signed/modinv.rs | 72 +++++++++++++++++++++++++++++++++++++++- test-generator/Invocs.hs | 28 ++++++++-------- test-generator/Tests.hs | 21 ++++++++++-- 3 files changed, 105 insertions(+), 16 deletions(-) diff --git a/src/signed/modinv.rs b/src/signed/modinv.rs index 486815f..0b1597a 100644 --- a/src/signed/modinv.rs +++ b/src/signed/modinv.rs @@ -6,6 +6,33 @@ pub trait ModInv: Sized { fn modinv(&self, phi: &Self) -> Option; } +macro_rules! smodinv_impls { + ($name: ident, $bigger: ident) => { + impl ModInv for $name { + fn modinv(&self, phi: &$name) -> Option<$name> + { + let (_, mut b, g) = phi.egcd(&self); + + if g != $bigger::from(1u64) { + return None; + } + + let bigphi = $bigger::from(phi); + + while b.is_negative() { + b += &bigphi; + } + + if b > bigphi { + b -= &bigphi; + } + + Some($name::from(b)) + } + } + } +} + macro_rules! modinv_impls { ($name: ident, $sname: ident, $uname: ident) => { impl ModInv for $name { @@ -17,8 +44,14 @@ macro_rules! modinv_impls { return None; } + let sphi = $sname::from($uname::from(phi)); + while b.is_negative() { - b += $sname::from($uname::from(phi)); + b += &sphi; + } + + if b > sphi { + b -= &sphi; } Some($name::from($uname::from(b))) @@ -54,6 +87,43 @@ macro_rules! generate_modinv_tests { let b = $tname::from_bytes(bbytes); let c = $tname::from_bytes(cbytes); + match a.modinv(&b) { + None => assert!(false), + Some(myc) => { + assert_eq!(c, myc); + } + } + }); + }; +} + +#[cfg(test)] +macro_rules! generate_smodinv_tests { + ($sname: ident, $tname: ident, $mname: ident) => { + #[test] + fn $mname() { + generate_smodinv_tests!(body $sname, $tname, $mname); + } + }; + (ignore $sname: ident, $tname: ident, $mname: ident) => { + #[test] + #[ignore] + fn $mname() { + generate_smodinv_tests!(body $sname, $tname, $mname); + } + }; + (body $sname: ident, $tname: ident, $mname: ident) => { + let fname = build_test_path("modinv", stringify!($sname)); + run_test(fname.to_string(), 3, |case| { + let (nega, abytes) = case.get("a").unwrap(); + let (negb, bbytes) = case.get("b").unwrap(); + let (negc, cbytes) = case.get("c").unwrap(); + + assert!(!negb && !negc); + let a = $sname::new(*nega, $tname::from_bytes(abytes)); + let b = $sname::new(false, $tname::from_bytes(bbytes)); + let c = $sname::new(false, $tname::from_bytes(cbytes)); + match a.modinv(&b) { None => assert!(false), Some(myc) => { diff --git a/test-generator/Invocs.hs b/test-generator/Invocs.hs index 136b345..44fafae 100644 --- a/test-generator/Invocs.hs +++ b/test-generator/Invocs.hs @@ -101,7 +101,8 @@ main = do SignedSub -> hPutStrLn hndl ("subtraction_impls!(I" ++ show size ++ ", I" ++ show (size + 64) ++ ", U" ++ show (size + 64) ++ ");") SignedMul -> hPutStrLn hndl ("mul_impls!(I" ++ show size ++ ", I" ++ show (size * 2) ++ ");") SignedDiv -> hPutStrLn hndl ("div_impls!(I" ++ show size ++ ", U" ++ show size ++ ");") - EGCD -> hPutStrLn hndl ("egcd_impls!(I" ++ show (size + 64) ++ ", U" ++ show size ++ ", I" ++ show size ++ ");") + SignedModInv -> hPutStrLn hndl ("smodinv_impls!(I" ++ show size ++ ", I" ++ show (size + 64) ++ ");") + EGCD -> hPutStrLn hndl ("egcd_impls!(I" ++ show (size + 64) ++ ", U" ++ show size ++ ", I" ++ show size ++ ", I" ++ show ((size + 64) * 2) ++ ");") ModDiv -> hPutStrLn hndl ("moddiv_impls!(I" ++ show size ++ ", I" ++ show (size * 2) ++ ");") ModInv -> hPutStrLn hndl ("modinv_impls!(U" ++ show size ++ ", I" ++ show (size + 64) ++ ", U" ++ show (size + 64) ++ ");") SigConvert v -> hPutStrLn hndl ("conversion_impls!(I" ++ show size ++ ", U" ++ show size ++ ", I" ++ show v ++ ", U" ++ show v ++ ");") @@ -109,16 +110,17 @@ main = do hPutStrLn hndl "" hPutStrLn hndl "\n#[cfg(test)]" hPutStrLn hndl "mod tests {" - generateSigTestBlock hndl "sigadd" SignedAdd True 16384 [(+ 64)] [(+ 64)] - generateSigTestBlock hndl "sigsub" SignedSub True 16384 [(+ 64)] [(+ 64)] - generateSigTestBlock hndl "signed" SignedBase True 90000 [] [] - generateSigTestBlock hndl "sigconversion" SignedBase False 90000 [] [] - generateSigTestBlock hndl "sigcmp" SignedCmp True 90000 [] [] - generateSigTestBlock hndl "sigmul" SignedMul True 9000 [(* 2)] [(* 2)] - generateSigTestBlock hndl "sigdiv" SignedDiv True 2049 [] [] - generateSigTestBlock hndl "sigshiftl" SignedShift True 16384 [] [] - generateSigTestBlock hndl "sigshiftr" SignedShift True 16384 [] [] - generateSigTestBlock hndl "egcd" EGCD True 1024 [(+ 64)] [(+ 64)] - generateSigTestBlock hndl "moddiv" ModDiv True 2048 [] [] - generateSigTestBlock hndl "modinv" ModInv True 2048 [] [] + generateSigTestBlock hndl "sigadd" SignedAdd True 16384 [(+ 64)] [(+ 64)] + generateSigTestBlock hndl "sigsub" SignedSub True 16384 [(+ 64)] [(+ 64)] + generateSigTestBlock hndl "signed" SignedBase True 90000 [] [] + generateSigTestBlock hndl "sigconversion" SignedBase False 90000 [] [] + generateSigTestBlock hndl "sigcmp" SignedCmp True 90000 [] [] + generateSigTestBlock hndl "sigmul" SignedMul True 9000 [(* 2)] [(* 2)] + generateSigTestBlock hndl "sigdiv" SignedDiv True 2049 [] [] + generateSigTestBlock hndl "sigshiftl" SignedShift True 16384 [] [] + generateSigTestBlock hndl "sigshiftr" SignedShift True 16384 [] [] + generateSigTestBlock hndl "egcd" EGCD True 1024 [(+ 64)] [(+ 64)] + generateSigTestBlock hndl "moddiv" ModDiv True 2048 [] [] + generateSigTestBlock hndl "modinv" ModInv True 2048 [] [] + generateSigTestBlock hndl "smodinv" SignedModInv True 2048 [] [] hPutStrLn hndl "}" diff --git a/test-generator/Tests.hs b/test-generator/Tests.hs index f83b0cf..2fb1d85 100644 --- a/test-generator/Tests.hs +++ b/test-generator/Tests.hs @@ -309,5 +309,22 @@ modinvTest size memoryIn = ("c", showX c)] in if c == 0 then attempt memory2 - else assert ((c < b) && ((a * c) `mod` b == 1)) (res, c, memory2) - in attempt memoryIn \ No newline at end of file + else assert (c < b) $ + assert ((a * c) `mod` b == 1) $ + (res, c, memory2) + in attempt memoryIn + +smodinvTest :: Test +smodinvTest size memoryIn = + let attempt memory0 = + let (a, memory1) = genSign (generateNum memory0 "a" size) + (b, memory2) = generateNum memory1 "b" size + c = recipModInteger a b + res = Map.fromList [("a", showX a), ("b", showX b), + ("c", showX c)] + in if c == 0 + then attempt memory2 + else assert (c < b) $ + assert ((a * c) `mod` b == 1) $ + (res, c, memory2) + in attempt memoryIn