Add support for signed modular inversion.
This commit is contained in:
@@ -6,6 +6,33 @@ pub trait ModInv: Sized {
|
|||||||
fn modinv(&self, phi: &Self) -> Option<Self>;
|
fn modinv(&self, phi: &Self) -> Option<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
macro_rules! modinv_impls {
|
||||||
($name: ident, $sname: ident, $uname: ident) => {
|
($name: ident, $sname: ident, $uname: ident) => {
|
||||||
impl ModInv for $name {
|
impl ModInv for $name {
|
||||||
@@ -17,8 +44,14 @@ macro_rules! modinv_impls {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let sphi = $sname::from($uname::from(phi));
|
||||||
|
|
||||||
while b.is_negative() {
|
while b.is_negative() {
|
||||||
b += $sname::from($uname::from(phi));
|
b += &sphi;
|
||||||
|
}
|
||||||
|
|
||||||
|
if b > sphi {
|
||||||
|
b -= &sphi;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some($name::from($uname::from(b)))
|
Some($name::from($uname::from(b)))
|
||||||
@@ -63,3 +96,40 @@ macro_rules! generate_modinv_tests {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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) => {
|
||||||
|
assert_eq!(c, myc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -101,7 +101,8 @@ main = do
|
|||||||
SignedSub -> hPutStrLn hndl ("subtraction_impls!(I" ++ show size ++ ", I" ++ show (size + 64) ++ ", U" ++ show (size + 64) ++ ");")
|
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) ++ ");")
|
SignedMul -> hPutStrLn hndl ("mul_impls!(I" ++ show size ++ ", I" ++ show (size * 2) ++ ");")
|
||||||
SignedDiv -> hPutStrLn hndl ("div_impls!(I" ++ show size ++ ", U" ++ show size ++ ");")
|
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) ++ ");")
|
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) ++ ");")
|
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 ++ ");")
|
SigConvert v -> hPutStrLn hndl ("conversion_impls!(I" ++ show size ++ ", U" ++ show size ++ ", I" ++ show v ++ ", U" ++ show v ++ ");")
|
||||||
@@ -121,4 +122,5 @@ main = do
|
|||||||
generateSigTestBlock hndl "egcd" EGCD True 1024 [(+ 64)] [(+ 64)]
|
generateSigTestBlock hndl "egcd" EGCD True 1024 [(+ 64)] [(+ 64)]
|
||||||
generateSigTestBlock hndl "moddiv" ModDiv True 2048 [] []
|
generateSigTestBlock hndl "moddiv" ModDiv True 2048 [] []
|
||||||
generateSigTestBlock hndl "modinv" ModInv True 2048 [] []
|
generateSigTestBlock hndl "modinv" ModInv True 2048 [] []
|
||||||
|
generateSigTestBlock hndl "smodinv" SignedModInv True 2048 [] []
|
||||||
hPutStrLn hndl "}"
|
hPutStrLn hndl "}"
|
||||||
|
|||||||
@@ -309,5 +309,22 @@ modinvTest size memoryIn =
|
|||||||
("c", showX c)]
|
("c", showX c)]
|
||||||
in if c == 0
|
in if c == 0
|
||||||
then attempt memory2
|
then attempt memory2
|
||||||
else assert ((c < b) && ((a * c) `mod` b == 1)) (res, c, memory2)
|
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
|
in attempt memoryIn
|
||||||
Reference in New Issue
Block a user