Division / modulus!

This commit is contained in:
2020-01-07 18:51:29 -10:00
parent c1d2922ab2
commit 2888164814
4 changed files with 226 additions and 0 deletions

View File

@@ -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

View File

@@ -35,6 +35,7 @@ library
Compare,
Conversions,
CryptoNum,
Division,
File,
Gen,
Generators,

213
generation/src/Division.hs Normal file
View 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)