Division / modulus!
This commit is contained in:
@@ -8,6 +8,7 @@ import Compare(comparisons)
|
||||
import Conversions(conversions)
|
||||
import CryptoNum(cryptoNum)
|
||||
import Control.Monad(forM_,unless)
|
||||
import Division(divisionOps)
|
||||
import File(File,Task(..),generateTasks)
|
||||
import Multiply(safeMultiplyOps, unsafeMultiplyOps)
|
||||
import Scale(safeScaleOps, unsafeScaleOps)
|
||||
@@ -36,6 +37,7 @@ unsignedFiles = [
|
||||
, comparisons
|
||||
, conversions
|
||||
, cryptoNum
|
||||
, divisionOps
|
||||
, safeAddOps
|
||||
, safeMultiplyOps
|
||||
, safeScaleOps
|
||||
|
||||
@@ -35,6 +35,7 @@ library
|
||||
Compare,
|
||||
Conversions,
|
||||
CryptoNum,
|
||||
Division,
|
||||
File,
|
||||
Gen,
|
||||
Generators,
|
||||
|
||||
213
generation/src/Division.hs
Normal file
213
generation/src/Division.hs
Normal file
@@ -0,0 +1,213 @@
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
module Division(divisionOps)
|
||||
where
|
||||
|
||||
import Data.Map.Strict(Map)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import File
|
||||
import Gen(toLit)
|
||||
import Generators
|
||||
import Language.Rust.Data.Ident
|
||||
import Language.Rust.Data.Position
|
||||
import Language.Rust.Quote
|
||||
import Language.Rust.Syntax
|
||||
import System.Random(RandomGen)
|
||||
|
||||
numTestCases :: Int
|
||||
numTestCases = 3000
|
||||
|
||||
divisionOps :: File
|
||||
divisionOps = File {
|
||||
predicate = \ _ _ -> True,
|
||||
outputName = "divmod",
|
||||
isUnsigned = True,
|
||||
generator = declareDivision,
|
||||
testCase = Just generateDivisionTests
|
||||
}
|
||||
|
||||
declareDivision :: Word -> SourceFile Span
|
||||
declareDivision size =
|
||||
let sname = mkIdent ("U" ++ show size)
|
||||
entries = size `div` 64
|
||||
copyAssign = map doCopy [0..entries-1]
|
||||
testFileLit = Lit [] (Str (testFile size) Cooked Unsuffixed mempty) mempty
|
||||
in [sourceFile|
|
||||
use core::ops::{Div, DivAssign};
|
||||
use core::ops::{Rem, RemAssign};
|
||||
#[cfg(test)]
|
||||
use crate::CryptoNum;
|
||||
#[cfg(test)]
|
||||
use crate::testing::{build_test_path,run_test};
|
||||
use crate::unsigned::$$sname;
|
||||
use super::super::super::DivMod;
|
||||
|
||||
impl DivMod for $$sname {
|
||||
fn divmod(&self, rhs: &$$sname) -> ($$sname, $$sname) {
|
||||
let mut q = $$sname::zero();
|
||||
let mut r = $$sname::zero();
|
||||
|
||||
for (ndigit, qdigit) in self.value.iter().rev().zip(q.value.iter_mut().rev()) {
|
||||
for i in (0..64).rev() {
|
||||
let mut r1: $$sname = &r << 1u64;
|
||||
r1.value[0] |= (ndigit >> i) & 1u64;
|
||||
let mut r2: $$sname = r1.clone();
|
||||
r2 -= rhs;
|
||||
let (newr, bit) = if &r1 > rhs {
|
||||
(r2, 1)
|
||||
} else {
|
||||
(r1, 0)
|
||||
};
|
||||
r = newr;
|
||||
*qdigit |= bit << i;
|
||||
}
|
||||
}
|
||||
|
||||
(q, r)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for $$sname {
|
||||
type Output = $$sname;
|
||||
|
||||
fn div(self, rhs: $$sname) -> Self::Output {
|
||||
let (res, _) = self.divmod(&rhs);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Div<$$sname> for &'a $$sname {
|
||||
type Output = $$sname;
|
||||
|
||||
fn div(self, rhs: $$sname) -> Self::Output {
|
||||
let (res, _) = self.divmod(&rhs);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Div<&'a $$sname> for $$sname {
|
||||
type Output = $$sname;
|
||||
|
||||
fn div(self, rhs: &$$sname) -> Self::Output {
|
||||
let (res, _) = self.divmod(rhs);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Div<&'a $$sname> for &'b $$sname {
|
||||
type Output = $$sname;
|
||||
|
||||
fn div(self, rhs: &$$sname) -> Self::Output {
|
||||
let (res, _) = self.divmod(rhs);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign for $$sname {
|
||||
fn div_assign(&mut self, rhs: $$sname) {
|
||||
let (res, _) = self.divmod(&rhs);
|
||||
$@{copyAssign}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DivAssign<&'a $$sname> for $$sname {
|
||||
fn div_assign(&mut self, rhs: &$$sname) {
|
||||
let (res, _) = self.divmod(rhs);
|
||||
$@{copyAssign}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rem for $$sname {
|
||||
type Output = $$sname;
|
||||
|
||||
fn rem(self, rhs: $$sname) -> Self::Output {
|
||||
let (_, res) = self.divmod(&rhs);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Rem<$$sname> for &'a $$sname {
|
||||
type Output = $$sname;
|
||||
|
||||
fn rem(self, rhs: $$sname) -> Self::Output {
|
||||
let (_, res) = self.divmod(&rhs);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Rem<&'a $$sname> for $$sname {
|
||||
type Output = $$sname;
|
||||
|
||||
fn rem(self, rhs: &$$sname) -> Self::Output {
|
||||
let (_, res) = self.divmod(rhs);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a,'b> Rem<&'a $$sname> for &'b $$sname {
|
||||
type Output = $$sname;
|
||||
|
||||
fn rem(self, rhs: &$$sname) -> Self::Output {
|
||||
let (_, res) = self.divmod(rhs);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl RemAssign for $$sname {
|
||||
fn rem_assign(&mut self, rhs: $$sname) {
|
||||
let (_, res) = self.divmod(&rhs);
|
||||
$@{copyAssign}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RemAssign<&'a $$sname> for $$sname {
|
||||
fn rem_assign(&mut self, rhs: &$$sname) {
|
||||
let (_, res) = self.divmod(rhs);
|
||||
$@{copyAssign}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(non_snake_case)]
|
||||
#[test]
|
||||
fn KATs() {
|
||||
run_test(build_test_path("divmod", $$(testFileLit)), 4, |case| {
|
||||
let (neg0, xbytes) = case.get("x").unwrap();
|
||||
let (neg1, ybytes) = case.get("y").unwrap();
|
||||
let (neg2, zbytes) = case.get("z").unwrap();
|
||||
let (neg3, rbytes) = case.get("r").unwrap();
|
||||
|
||||
assert!(!neg0 && !neg1 && !neg2 && !neg3);
|
||||
let x = $$sname::from_bytes(&xbytes);
|
||||
let y = $$sname::from_bytes(&ybytes);
|
||||
let z = $$sname::from_bytes(&zbytes);
|
||||
let r = $$sname::from_bytes(&rbytes);
|
||||
|
||||
let (myz, myr) = x.divmod(&y);
|
||||
|
||||
assert_eq!(z, myz);
|
||||
assert_eq!(r, myr);
|
||||
assert_eq!(z, &x / &y);
|
||||
assert_eq!(r, &x % &y);
|
||||
});
|
||||
}
|
||||
|]
|
||||
|
||||
doCopy :: Word -> Stmt Span
|
||||
doCopy i =
|
||||
let liti = toLit i
|
||||
in [stmt| self.value[$$(liti)] = res.value[$$(liti)]; |]
|
||||
|
||||
generateDivisionTests :: RandomGen g => Word -> g -> [Map String String]
|
||||
generateDivisionTests size g = go g numTestCases
|
||||
where
|
||||
go _ 0 = []
|
||||
go g0 i =
|
||||
let (x, g1) = generateNum g0 size
|
||||
(y, g2) = generateNum g1 size
|
||||
tcase = Map.fromList [("x", showX x), ("y", showX y),
|
||||
("z", showX (x `div` y)),
|
||||
("r", showX (x `mod` y))]
|
||||
in if y == 0
|
||||
then go g0 i
|
||||
else tcase : go g2 (i - 1)
|
||||
|
||||
10
src/lib.rs
10
src/lib.rs
@@ -38,6 +38,16 @@ pub trait CryptoNum {
|
||||
fn to_bytes(&self, bytes: &mut [u8]);
|
||||
}
|
||||
|
||||
/// Provides the ability to do a simultaneous division and modulus operation;
|
||||
/// this is used as the implementation of division and multiplication, and
|
||||
/// so you can save time doing both at once if you need them.
|
||||
///
|
||||
pub trait DivMod: Sized {
|
||||
/// Divide and modulus as a single operation. The first element of the tuple
|
||||
/// is the quotient, the second is the modulus.
|
||||
fn divmod(&self, rhs: &Self) -> (Self, Self);
|
||||
}
|
||||
|
||||
/// An error in conversion of large numbers (either to primitives or to other numbers
|
||||
#[derive(Debug)]
|
||||
pub enum ConversionError {
|
||||
|
||||
Reference in New Issue
Block a user