implement division and remainder operations

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2024-05-08 23:21:16 +02:00
parent 864e40901f
commit 6af889c1ff
6 changed files with 310 additions and 173 deletions
@@ -0,0 +1,25 @@
contract DivisionArithmetics {
function div(uint n, uint d) public pure returns (uint q) {
assembly {
q := div(n, d)
}
}
function sdiv(int n, int d) public pure returns (int q) {
assembly {
q := sdiv(n, d)
}
}
function mod(uint n, uint d) public pure returns (uint r) {
assembly {
r := mod(n, d)
}
}
function smod(int n, int d) public pure returns (int r) {
assembly {
r := smod(n, d)
}
}
}
+57 -1
View File
@@ -1,4 +1,4 @@
use alloy_primitives::U256;
use alloy_primitives::{I256, U256};
use alloy_sol_types::{sol, SolCall};
#[derive(Clone)]
@@ -83,6 +83,18 @@ sol!(
}
);
sol!(
contract DivisionArithmetics {
function div(uint n, uint d) public pure returns (uint q);
function sdiv(int n, int d) public pure returns (int q);
function mod(uint n, uint d) public pure returns (uint r);
function smod(int n, int d) public pure returns (int r);
}
);
impl Contract {
pub fn baseline() -> Self {
let code = include_str!("../contracts/Baseline.sol");
@@ -226,6 +238,50 @@ impl Contract {
calldata: Context::callerCall::new(()).abi_encode(),
}
}
pub fn division_arithmetics_div(n: U256, d: U256) -> Self {
let code = include_str!("../contracts/DivisionArithmetics.sol");
let name = "DivisionArithmetics";
Self {
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: DivisionArithmetics::divCall::new((n, d)).abi_encode(),
}
}
pub fn division_arithmetics_sdiv(n: I256, d: I256) -> Self {
let code = include_str!("../contracts/DivisionArithmetics.sol");
let name = "DivisionArithmetics";
Self {
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: DivisionArithmetics::sdivCall::new((n, d)).abi_encode(),
}
}
pub fn division_arithmetics_mod(n: U256, d: U256) -> Self {
let code = include_str!("../contracts/DivisionArithmetics.sol");
let name = "DivisionArithmetics";
Self {
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: DivisionArithmetics::modCall::new((n, d)).abi_encode(),
}
}
pub fn division_arithmetics_smod(n: I256, d: I256) -> Self {
let code = include_str!("../contracts/DivisionArithmetics.sol");
let name = "DivisionArithmetics";
Self {
evm_runtime: crate::compile_evm_bin_runtime(name, code),
pvm_runtime: crate::compile_blob(name, code),
calldata: DivisionArithmetics::smodCall::new((n, d)).abi_encode(),
}
}
}
#[cfg(test)]
+96
View File
@@ -296,3 +296,99 @@ fn caller() {
let expected = Address::from(&mock_runtime::State::CALLER);
assert_eq!(received, expected);
}
#[test]
fn unsigned_division() {
let one = U256::from(1);
let two = U256::from(2);
let five = U256::from(5);
for (n, d, q) in [
(five, five, one),
(five, one, five),
(U256::ZERO, U256::MAX, U256::ZERO),
(five, two, two),
(one, U256::ZERO, U256::ZERO),
] {
let state = assert_success(Contract::division_arithmetics_div(n, d), true);
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
assert_eq!(received, q);
}
}
#[test]
fn signed_division() {
let one = I256::try_from(1).unwrap();
let two = I256::try_from(2).unwrap();
let minus_two = I256::try_from(-2).unwrap();
let five = I256::try_from(5).unwrap();
let minus_five = I256::try_from(-5).unwrap();
for (n, d, q) in [
(five, five, one),
(five, one, five),
(I256::ZERO, I256::MAX, I256::ZERO),
(I256::ZERO, I256::MINUS_ONE, I256::ZERO),
(five, two, two),
(five, I256::MINUS_ONE, minus_five),
(I256::MINUS_ONE, minus_two, I256::ZERO),
(minus_five, minus_five, one),
(minus_five, two, minus_two),
(I256::MINUS_ONE, I256::MIN, I256::ZERO),
(one, I256::ZERO, I256::ZERO),
] {
let state = assert_success(Contract::division_arithmetics_sdiv(n, d), true);
let received = I256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
assert_eq!(received, q);
}
}
#[test]
fn unsigned_remainder() {
let one = U256::from(1);
let two = U256::from(2);
let five = U256::from(5);
for (n, d, q) in [
(five, five, U256::ZERO),
(five, one, U256::ZERO),
(U256::ZERO, U256::MAX, U256::ZERO),
(U256::MAX, U256::MAX, U256::ZERO),
(five, two, one),
(two, five, two),
(U256::MAX, U256::ZERO, U256::ZERO),
] {
let state = assert_success(Contract::division_arithmetics_mod(n, d), true);
let received = U256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
assert_eq!(received, q);
}
}
#[test]
fn signed_remainder() {
let one = I256::try_from(1).unwrap();
let two = I256::try_from(2).unwrap();
let minus_two = I256::try_from(-2).unwrap();
let five = I256::try_from(5).unwrap();
let minus_five = I256::try_from(-5).unwrap();
for (n, d, q) in [
(five, five, I256::ZERO),
(five, one, I256::ZERO),
(I256::ZERO, I256::MAX, I256::ZERO),
(I256::MAX, I256::MAX, I256::ZERO),
(five, two, one),
(two, five, two),
(five, minus_five, I256::ZERO),
(five, I256::MINUS_ONE, I256::ZERO),
(five, minus_two, one),
(minus_five, two, I256::MINUS_ONE),
(minus_two, five, minus_two),
(minus_five, minus_five, I256::ZERO),
(minus_five, I256::MINUS_ONE, I256::ZERO),
(minus_five, minus_two, I256::MINUS_ONE),
(minus_two, minus_five, minus_two),
(I256::MIN, I256::MINUS_ONE, I256::ZERO),
(I256::ZERO, I256::ZERO, I256::ZERO),
] {
let state = assert_success(Contract::division_arithmetics_smod(n, d), true);
let received = I256::from_be_bytes::<32>(state.output.data.try_into().unwrap());
assert_eq!(received, q);
}
}