feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
@@ -0,0 +1,48 @@
[package]
name = "pezsp-arithmetic"
version = "23.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Minimal fixed point arithmetic primitives and types for runtime."
documentation = "https://docs.rs/pezsp-arithmetic"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[[bench]]
name = "bench"
harness = false
[dependencies]
codec = { features = ["derive", "max-encoded-len"], workspace = true }
docify = { workspace = true }
integer-sqrt = { workspace = true }
num-traits = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
serde = { features = ["alloc", "derive"], optional = true, workspace = true }
static_assertions = { workspace = true, default-features = true }
[dev-dependencies]
criterion = { workspace = true, default-features = true }
primitive-types = { workspace = true, default-features = true }
rand = { workspace = true, default-features = true }
pezsp-crypto-hashing = { workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"codec/std",
"num-traits/std",
"scale-info/std",
"serde/std",
"pezsp-crypto-hashing/std",
]
# Serde support without relying on std features.
serde = ["dep:serde", "scale-info/serde"]
@@ -0,0 +1,3 @@
Minimal fixed point arithmetic primitives and types for runtime.
License: Apache-2.0
@@ -0,0 +1,81 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use rand::Rng;
use pezsp_arithmetic::biguint::{BigUint, Single};
fn random_big_uint(size: usize) -> BigUint {
let mut rng = rand::thread_rng();
let digits: Vec<_> = (0..size).map(|_| rng.gen_range(0..Single::MAX)).collect();
BigUint::from_limbs(&digits)
}
fn bench_op<F: Fn(&BigUint, &BigUint)>(c: &mut Criterion, name: &str, op: F) {
let mut group = c.benchmark_group(name);
for size in [2, 4, 6, 8, 10].iter() {
group.throughput(Throughput::Elements(*size));
group.bench_with_input(BenchmarkId::from_parameter(size), size, |bencher, &size| {
let a = random_big_uint(size as usize);
let b = random_big_uint(size as usize);
bencher.iter(|| op(&a, &b));
});
}
}
fn bench_addition(c: &mut Criterion) {
bench_op(c, "addition", |a, b| {
let _ = a.clone().add(b);
});
}
fn bench_subtraction(c: &mut Criterion) {
bench_op(c, "subtraction", |a, b| {
let _ = a.clone().sub(b);
});
}
fn bench_multiplication(c: &mut Criterion) {
bench_op(c, "multiplication", |a, b| {
let _ = a.clone().mul(b);
});
}
fn bench_division(c: &mut Criterion) {
let mut group = c.benchmark_group("division");
for size in [4, 6, 8, 10].iter() {
group.throughput(Throughput::Elements(*size));
group.bench_with_input(BenchmarkId::from_parameter(size), size, |bencher, &size| {
let a = random_big_uint(size as usize);
let b = random_big_uint(rand::thread_rng().gen_range(2..size as usize));
bencher.iter(|| {
let _ = a.clone().div(&b, true);
});
});
}
}
criterion_group! {
name = benches;
config = Criterion::default();
targets = bench_addition, bench_subtraction, bench_multiplication, bench_division
}
criterion_main!(benches);
@@ -0,0 +1,48 @@
[package]
name = "pezsp-arithmetic-fuzzer"
version = "2.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Fuzzer for fixed point arithmetic primitives."
documentation = "https://docs.rs/pezsp-arithmetic-fuzzer"
publish = false
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[[bin]]
name = "biguint"
path = "src/biguint.rs"
[[bin]]
name = "normalize"
path = "src/normalize.rs"
[[bin]]
name = "per_thing_from_rational"
path = "src/per_thing_from_rational.rs"
[[bin]]
name = "per_thing_mult_fraction"
path = "src/per_thing_mult_fraction.rs"
[[bin]]
name = "multiply_by_rational_with_rounding"
path = "src/multiply_by_rational_with_rounding.rs"
[[bin]]
name = "fixed_point"
path = "src/fixed_point.rs"
[dependencies]
arbitrary = { workspace = true }
fraction = { workspace = true }
honggfuzz = { workspace = true }
num-bigint = { workspace = true }
pezsp-arithmetic = { workspace = true, default-features = true }
@@ -0,0 +1,211 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Running
//! Running this fuzzer can be done with `cargo hfuzz run biguint`. `honggfuzz` CLI options can
//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
//!
//! # Debugging a panic
//! Once a panic is found, it can be debugged with
//! `cargo hfuzz run-debug biguint hfuzz_workspace/biguint/*.fuzz`.
//!
//! # More information
//! More information about `honggfuzz` can be found
//! [here](https://docs.rs/honggfuzz/).
use honggfuzz::fuzz;
use pezsp_arithmetic::biguint::{BigUint, Single};
fn main() {
loop {
fuzz!(|data: (Vec<Single>, Vec<Single>, bool)| {
let (mut digits_u, mut digits_v, return_remainder) = data;
let mut u = BigUint::from_limbs(&digits_u);
let mut v = BigUint::from_limbs(&digits_v);
u.lstrip();
v.lstrip();
let ue = u128::try_from(u.clone());
let ve = u128::try_from(v.clone());
digits_u.reverse();
digits_v.reverse();
let num_u = num_bigint::BigUint::new(digits_u);
let num_v = num_bigint::BigUint::new(digits_v);
if check_digit_lengths(&u, &v, 4) {
assert_eq!(u.cmp(&v), ue.cmp(&ve));
assert_eq!(u.eq(&v), ue.eq(&ve));
}
if check_digit_lengths(&u, &v, 3) {
let expected = ue.unwrap() + ve.unwrap();
let t = u.clone().add(&v);
assert_eq!(
u128::try_from(t.clone()).unwrap(),
expected,
"{:?} + {:?} ===> {:?} != {:?}",
u,
v,
t,
expected,
);
}
if check_digit_lengths(&u, &v, 4) {
let expected = ue.unwrap().checked_sub(ve.unwrap());
let t = u.clone().sub(&v);
if expected.is_none() {
assert!(t.is_err())
} else {
let t = t.unwrap();
let expected = expected.unwrap();
assert_eq!(
u128::try_from(t.clone()).unwrap(),
expected,
"{:?} - {:?} ===> {:?} != {:?}",
u,
v,
t,
expected,
);
}
}
if check_digit_lengths(&u, &v, 2) {
let expected = ue.unwrap() * ve.unwrap();
let t = u.clone().mul(&v);
assert_eq!(
u128::try_from(t.clone()).unwrap(),
expected,
"{:?} * {:?} ===> {:?} != {:?}",
u,
v,
t,
expected,
);
}
if check_digit_lengths(&u, &v, 4) {
let (ue, ve) = (ue.unwrap(), ve.unwrap());
if ve == 0 {
return;
}
let (q, r) = (ue / ve, ue % ve);
if let Some((qq, rr)) = u.clone().div(&v, true) {
assert_eq!(
u128::try_from(qq.clone()).unwrap(),
q,
"{:?} / {:?} ===> {:?} != {:?}",
u,
v,
qq,
q,
);
assert_eq!(
u128::try_from(rr.clone()).unwrap(),
r,
"{:?} % {:?} ===> {:?} != {:?}",
u,
v,
rr,
r,
);
} else if v.len() == 1 {
let qq = u.clone().div_unit(ve as Single);
assert_eq!(
u128::try_from(qq.clone()).unwrap(),
q,
"[single] {:?} / {:?} ===> {:?} != {:?}",
u,
v,
qq,
q,
);
} else if v.msb() != 0 && u.msb() != 0 && u.len() > v.len() {
panic!("div returned none for an unexpected reason");
}
}
// Test against num_bigint
// Equality
assert_eq!(u.cmp(&v), num_u.cmp(&num_v));
// Addition
let w = u.clone().add(&v);
let num_w = num_u.clone() + &num_v;
assert_biguints_eq(&w, &num_w);
// Subtraction
if let Ok(w) = u.clone().sub(&v) {
let num_w = num_u.clone() - &num_v;
assert_biguints_eq(&w, &num_w);
}
// Multiplication
let w = u.clone().mul(&v);
let num_w = num_u.clone() * &num_v;
assert_biguints_eq(&w, &num_w);
// Division
if v.len() == 1 && v.get(0) != 0 {
let w = u.div_unit(v.get(0));
let num_w = num_u / &num_v;
assert_biguints_eq(&w, &num_w);
} else if u.len() > v.len() && v.len() > 1 {
let num_remainder = num_u.clone() % num_v.clone();
let (w, remainder) = u.div(&v, return_remainder).unwrap();
let num_w = num_u / &num_v;
assert_biguints_eq(&w, &num_w);
if return_remainder {
assert_biguints_eq(&remainder, &num_remainder);
}
}
});
}
}
fn check_digit_lengths(u: &BigUint, v: &BigUint, max_limbs: usize) -> bool {
1 <= u.len() && u.len() <= max_limbs && 1 <= v.len() && v.len() <= max_limbs
}
fn assert_biguints_eq(a: &BigUint, b: &num_bigint::BigUint) {
let mut a = a.clone();
a.lstrip();
// `num_bigint::BigUint` doesn't expose it's internals, so we need to convert into that to
// compare.
let limbs = (0..a.len()).map(|i| a.get(i)).collect();
let num_a = num_bigint::BigUint::new(limbs);
assert!(&num_a == b, "\narithmetic: {:?}\nnum-bigint: {:?}", a, b);
}
@@ -0,0 +1,84 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Running
//! Running this fuzzer can be done with `cargo hfuzz run fixed_point`. `honggfuzz` CLI options can
//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
//!
//! # Debugging a panic
//! Once a panic is found, it can be debugged with
//! `cargo hfuzz run-debug fixed_point hfuzz_workspace/fixed_point/*.fuzz`.
//!
//! # More information
//! More information about `honggfuzz` can be found
//! [here](https://docs.rs/honggfuzz/).
use honggfuzz::fuzz;
use pezsp_arithmetic::{traits::Saturating, FixedI64, FixedPointNumber};
fn main() {
loop {
fuzz!(|data: (i32, i32)| {
let x: i128 = data.0.into();
let y: i128 = data.1.into();
// Check `from_rational` and division are consistent.
if y != 0 {
let f1 =
FixedI64::saturating_from_integer(x) / FixedI64::saturating_from_integer(y);
let f2 = FixedI64::saturating_from_rational(x, y);
assert_eq!(f1.into_inner(), f2.into_inner());
}
// Check `saturating_mul`.
let a = FixedI64::saturating_from_rational(2, 5);
let b = a.saturating_mul(FixedI64::saturating_from_integer(x));
let n = b.into_inner() as i128;
let m = 2i128 * x * FixedI64::accuracy() as i128 / 5i128;
assert_eq!(n, m);
// Check `saturating_mul` and division are inverse.
if x != 0 {
assert_eq!(a, b / FixedI64::saturating_from_integer(x));
}
// Check `reciprocal`.
let r = a.reciprocal().unwrap().reciprocal().unwrap();
assert_eq!(a, r);
// Check addition.
let a = FixedI64::saturating_from_integer(x);
let b = FixedI64::saturating_from_integer(y);
let c = FixedI64::saturating_from_integer(x.saturating_add(y));
assert_eq!(a.saturating_add(b), c);
// Check subtraction.
let a = FixedI64::saturating_from_integer(x);
let b = FixedI64::saturating_from_integer(y);
let c = FixedI64::saturating_from_integer(x.saturating_sub(y));
assert_eq!(a.saturating_sub(b), c);
// Check `saturating_mul_acc_int`.
let a = FixedI64::saturating_from_rational(2, 5);
let b = a.saturating_mul_acc_int(x);
let xx = FixedI64::saturating_from_integer(x);
let d = a.saturating_mul(xx).saturating_add(xx).into_inner() as i128 /
FixedI64::accuracy() as i128;
assert_eq!(b, d);
});
}
}
@@ -0,0 +1,105 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Running
//! Running this fuzzer can be done with `cargo hfuzz run multiply_by_rational_with_rounding`.
//! `honggfuzz` CLI options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4
//! threads.
//!
//! # Debugging a panic
//! Once a panic is found, it can be debugged with
//! `cargo hfuzz run-debug multiply_by_rational_with_rounding
//! hfuzz_workspace/multiply_by_rational_with_rounding/*.fuzz`.
//!
//! # More information
//! More information about `honggfuzz` can be found
//! [here](https://docs.rs/honggfuzz/).
use fraction::prelude::BigFraction as Fraction;
use honggfuzz::fuzz;
use pezsp_arithmetic::{MultiplyRational, Rounding, Rounding::*};
/// Tries to demonstrate that `multiply_by_rational_with_rounding` is incorrect.
fn main() {
loop {
fuzz!(|data: (u128, u128, u128, ArbitraryRounding)| {
let (f, n, d, r) = (data.0, data.1, data.2, data.3 .0);
check::<u8>(f as u8, n as u8, d as u8, r);
check::<u16>(f as u16, n as u16, d as u16, r);
check::<u32>(f as u32, n as u32, d as u32, r);
check::<u64>(f as u64, n as u64, d as u64, r);
check::<u128>(f, n, d, r);
})
}
}
fn check<N>(f: N, n: N, d: N, r: Rounding)
where
N: MultiplyRational + Into<u128> + Copy + core::fmt::Debug,
{
let Some(got) = f.multiply_rational(n, d, r) else { return };
let (ae, be, ce) =
(Fraction::from(f.into()), Fraction::from(n.into()), Fraction::from(d.into()));
let want = round(ae * be / ce, r);
assert_eq!(
Fraction::from(got.into()),
want,
"{:?} * {:?} / {:?} = {:?} != {:?}",
f,
n,
d,
got,
want
);
}
/// Round a `Fraction` according to the given mode.
fn round(f: Fraction, r: Rounding) -> Fraction {
match r {
Up => f.ceil(),
NearestPrefUp =>
if f.fract() < Fraction::from(0.5) {
f.floor()
} else {
f.ceil()
},
Down => f.floor(),
NearestPrefDown =>
if f.fract() > Fraction::from(0.5) {
f.ceil()
} else {
f.floor()
},
}
}
/// An [`arbitrary::Arbitrary`] [`Rounding`] mode.
struct ArbitraryRounding(Rounding);
impl arbitrary::Arbitrary<'_> for ArbitraryRounding {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self(match u.int_in_range(0..=3).unwrap() {
0 => Up,
1 => NearestPrefUp,
2 => Down,
3 => NearestPrefDown,
_ => unreachable!(),
}))
}
}
@@ -0,0 +1,58 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Running
//! Running this fuzzer can be done with `cargo hfuzz run normalize`. `honggfuzz` CLI options can
//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
//!
//! # Debugging a panic
//! Once a panic is found, it can be debugged with
//! `cargo hfuzz run-debug normalize hfuzz_workspace/normalize/*.fuzz`.
use honggfuzz::fuzz;
use pezsp_arithmetic::Normalizable;
type Ty = u64;
fn main() {
let sum_limit = Ty::max_value() as u128;
let len_limit: usize = Ty::max_value().try_into().unwrap();
loop {
fuzz!(|data: (Vec<Ty>, Ty)| {
let (data, norm) = data;
if data.is_empty() {
return;
}
let pre_sum: u128 = data.iter().map(|x| *x as u128).sum();
let normalized = data.normalize(norm);
// error cases.
if pre_sum > sum_limit || data.len() > len_limit {
assert!(normalized.is_err())
} else if let Ok(normalized) = normalized {
// if sum goes beyond u128, panic.
let sum: u128 = normalized.iter().map(|x| *x as u128).sum();
// if this function returns Ok(), then it will ALWAYS be accurate.
assert_eq!(sum, norm as u128, "sums don't match {:?}, {}", normalized, norm);
} else {
panic!("Should have returned Ok for input = {:?}, target = {:?}", data, norm);
}
})
}
}
@@ -0,0 +1,105 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Running
//! Running this fuzzer can be done with `cargo hfuzz run per_thing_from_rational`. `honggfuzz` CLI
//! options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
//!
//! # Debugging a panic
//! Once a panic is found, it can be debugged with
//! `cargo hfuzz run-debug per_thing_from_rational hfuzz_workspace/per_thing_from_rational/*.fuzz`.
use fraction::prelude::BigFraction as Fraction;
use honggfuzz::fuzz;
use pezsp_arithmetic::{
traits::SaturatedConversion, PerThing, Perbill, Percent, Perquintill, Rounding::*, *,
};
/// Tries to demonstrate that `from_rational` is incorrect for any rounding modes.
///
/// NOTE: This `Fraction` library is really slow. Using f128/f256 does not work for the large
/// numbers. But an optimization could be done do use either floats or Fraction depending on the
/// size of the inputs.
fn main() {
loop {
fuzz!(|data: (u128, u128, ArbitraryRounding)| {
let (n, d, r) = (data.0.min(data.1), data.0.max(data.1).max(1), data.2);
check::<PerU16>(n, d, r.0);
check::<Percent>(n, d, r.0);
check::<Permill>(n, d, r.0);
check::<Perbill>(n, d, r.0);
check::<Perquintill>(n, d, r.0);
})
}
}
/// Assert that the parts of `from_rational` are correct for the given rounding mode.
fn check<Per: PerThing>(a: u128, b: u128, r: Rounding)
where
Per::Inner: Into<u128>,
{
let approx_ratio = Per::from_rational_with_rounding(a, b, r).unwrap();
let approx_parts = Fraction::from(approx_ratio.deconstruct().saturated_into::<u128>());
let perfect_ratio = if a == 0 && b == 0 {
Fraction::from(1)
} else {
Fraction::from(a) / Fraction::from(b.max(1))
};
let perfect_parts = round(perfect_ratio * Fraction::from(Per::ACCURACY.into()), r);
assert_eq!(
approx_parts, perfect_parts,
"approx_parts: {}, perfect_parts: {}, a: {}, b: {}",
approx_parts, perfect_parts, a, b
);
}
/// Round a `Fraction` according to the given mode.
fn round(f: Fraction, r: Rounding) -> Fraction {
match r {
Up => f.ceil(),
NearestPrefUp =>
if f.fract() < Fraction::from(0.5) {
f.floor()
} else {
f.ceil()
},
Down => f.floor(),
NearestPrefDown =>
if f.fract() > Fraction::from(0.5) {
f.ceil()
} else {
f.floor()
},
}
}
/// An [`arbitrary::Arbitrary`] [`Rounding`] mode.
struct ArbitraryRounding(Rounding);
impl arbitrary::Arbitrary<'_> for ArbitraryRounding {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(Self(match u.int_in_range(0..=3).unwrap() {
0 => Up,
1 => NearestPrefUp,
2 => Down,
3 => NearestPrefDown,
_ => unreachable!(),
}))
}
}
@@ -0,0 +1,69 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! # Running
//! Running this fuzzer can be done with `cargo hfuzz run per_thing_mult_fraction`. `honggfuzz` CLI
//! options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads.
//!
//! # Debugging a panic
//! Once a panic is found, it can be debugged with
//! `cargo hfuzz run-debug per_thing_mult_fraction hfuzz_workspace/per_thing_mult_fraction/*.fuzz`.
use honggfuzz::fuzz;
use pezsp_arithmetic::{PerThing, Perbill, Percent, Perquintill, *};
/// Tries to disprove `(n / d) * d <= n` for any `PerThing`s.
fn main() {
loop {
fuzz!(|data: (u128, u128)| {
let (n, d) = (data.0.min(data.1), data.0.max(data.1).max(1));
check_mul::<PerU16>(n, d);
check_mul::<Percent>(n, d);
check_mul::<Perbill>(n, d);
check_mul::<Perquintill>(n, d);
check_reciprocal_mul::<PerU16>(n, d);
check_reciprocal_mul::<Percent>(n, d);
check_reciprocal_mul::<Perbill>(n, d);
check_reciprocal_mul::<Perquintill>(n, d);
})
}
}
/// Checks that `(n / d) * d <= n`.
fn check_mul<P: PerThing>(n: u128, d: u128)
where
P: PerThing + core::ops::Mul<u128, Output = u128>,
{
let q = P::from_rational_with_rounding(n, d, Rounding::Down).unwrap();
assert!(q * d <= n, "{:?} * {:?} <= {:?}", q, d, n);
}
/// Checks that `n / (n / d) >= d`.
fn check_reciprocal_mul<P: PerThing>(n: u128, d: u128)
where
P: PerThing + core::ops::Mul<u128, Output = u128>,
{
let q = P::from_rational_with_rounding(n, d, Rounding::Down).unwrap();
if q.is_zero() {
return;
}
let r = q.saturating_reciprocal_mul_floor(n);
assert!(r >= d, "{} / ({} / {}) != {} but {}", n, n, d, d, r);
}
@@ -0,0 +1,755 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Infinite precision unsigned integer for bizinikiwi runtime.
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode};
use core::{cell::RefCell, cmp::Ordering, ops};
use num_traits::{One, Zero};
// A sensible value for this would be half of the dword size of the host machine. Since the
// runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively
// should yield the most performance.
/// Representation of a single limb.
pub type Single = u32;
/// Representation of two limbs.
pub type Double = u64;
/// Difference in the number of bits of [`Single`] and [`Double`].
const SHIFT: usize = 32;
/// short form of _Base_. Analogous to the value 10 in base-10 decimal numbers.
const B: Double = Single::max_value() as Double + 1;
static_assertions::const_assert!(
core::mem::size_of::<Double>() - core::mem::size_of::<Single>() == SHIFT / 8
);
/// Splits a [`Double`] limb number into a tuple of two [`Single`] limb numbers.
pub fn split(a: Double) -> (Single, Single) {
let al = a as Single;
let ah = (a >> SHIFT) as Single;
(ah, al)
}
/// Assumed as a given primitive.
///
/// Multiplication of two singles, which at most yields 1 double.
pub fn mul_single(a: Single, b: Single) -> Double {
let a: Double = a.into();
let b: Double = b.into();
a * b
}
/// Assumed as a given primitive.
///
/// Addition of two singles, which at most takes a single limb of result and a carry,
/// returned as a tuple respectively.
pub fn add_single(a: Single, b: Single) -> (Single, Single) {
let a: Double = a.into();
let b: Double = b.into();
let q = a + b;
let (carry, r) = split(q);
(r, carry)
}
/// Assumed as a given primitive.
///
/// Division of double by a single limb. Always returns a double limb of quotient and a single
/// limb of remainder.
fn div_single(a: Double, b: Single) -> (Double, Single) {
let b: Double = b.into();
let q = a / b;
let r = a % b;
// both conversions are trivially safe.
(q, r as Single)
}
/// Simple wrapper around an infinitely large integer, represented as limbs of [`Single`].
#[derive(Encode, Decode, Clone, Default)]
pub struct BigUint {
/// digits (limbs) of this number (sorted as msb -> lsb).
pub(crate) digits: Vec<Single>,
}
impl BigUint {
/// Create a new instance with `size` limbs. This prevents any number with zero limbs to be
/// created.
///
/// The behavior of the type is undefined with zero limbs.
pub fn with_capacity(size: usize) -> Self {
Self { digits: vec![0; size.max(1)] }
}
/// Raw constructor from custom limbs. If `limbs` is empty, `Zero::zero()` implementation is
/// used.
pub fn from_limbs(limbs: &[Single]) -> Self {
if !limbs.is_empty() {
Self { digits: limbs.to_vec() }
} else {
Zero::zero()
}
}
/// Number of limbs.
pub fn len(&self) -> usize {
self.digits.len()
}
/// A naive getter for limb at `index`. Note that the order is lsb -> msb.
///
/// #### Panics
///
/// This panics if index is out of range.
pub fn get(&self, index: usize) -> Single {
self.digits[self.len() - 1 - index]
}
/// A naive getter for limb at `index`. Note that the order is lsb -> msb.
pub fn checked_get(&self, index: usize) -> Option<Single> {
let i = self.len().checked_sub(1)?;
let j = i.checked_sub(index)?;
self.digits.get(j).cloned()
}
/// A naive setter for limb at `index`. Note that the order is lsb -> msb.
///
/// #### Panics
///
/// This panics if index is out of range.
pub fn set(&mut self, index: usize, value: Single) {
let len = self.digits.len();
self.digits[len - 1 - index] = value;
}
/// returns the least significant limb of the number.
///
/// #### Panics
///
/// While the constructor of the type prevents this, this can panic if `self` has no digits.
pub fn lsb(&self) -> Single {
self.digits[self.len() - 1]
}
/// returns the most significant limb of the number.
///
/// #### Panics
///
/// While the constructor of the type prevents this, this can panic if `self` has no digits.
pub fn msb(&self) -> Single {
self.digits[0]
}
/// Strips zeros from the left side (the most significant limbs) of `self`, if any.
pub fn lstrip(&mut self) {
// by definition, a big-int number should never have leading zero limbs. This function
// has the ability to cause this. There is nothing to do if the number already has 1
// limb only. call it a day and return.
if self.len().is_zero() {
return;
}
let index = self.digits.iter().position(|&elem| elem != 0).unwrap_or(self.len() - 1);
if index > 0 {
self.digits = self.digits[index..].to_vec()
}
}
/// Zero-pad `self` from left to reach `size` limbs. Will not make any difference if `self`
/// is already bigger than `size` limbs.
pub fn lpad(&mut self, size: usize) {
let n = self.len();
if n >= size {
return;
}
let pad = size - n;
let mut new_digits = (0..pad).map(|_| 0).collect::<Vec<Single>>();
new_digits.extend(self.digits.iter());
self.digits = new_digits;
}
/// Adds `self` with `other`. self and other do not have to have any particular size. Given
/// that the `n = max{size(self), size(other)}`, it will produce a number with `n + 1`
/// limbs.
///
/// This function does not strip the output and returns the original allocated `n + 1`
/// limbs. The caller may strip the output if desired.
///
/// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4.
pub fn add(self, other: &Self) -> Self {
let n = self.len().max(other.len());
let mut k: Double = 0;
let mut w = Self::with_capacity(n + 1);
for j in 0..n {
let u = Double::from(self.checked_get(j).unwrap_or(0));
let v = Double::from(other.checked_get(j).unwrap_or(0));
let s = u + v + k;
// proof: any number % B will fit into `Single`.
w.set(j, (s % B) as Single);
k = s / B;
}
// k is always 0 or 1.
w.set(n, k as Single);
w
}
/// Subtracts `other` from `self`. self and other do not have to have any particular size.
/// Given that the `n = max{size(self), size(other)}`, it will produce a number of size `n`.
///
/// If `other` is bigger than `self`, `Err(B - borrow)` is returned.
///
/// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4.
pub fn sub(self, other: &Self) -> Result<Self, Self> {
let n = self.len().max(other.len());
let mut k = 0;
let mut w = Self::with_capacity(n);
for j in 0..n {
let s = {
let u = Double::from(self.checked_get(j).unwrap_or(0));
let v = Double::from(other.checked_get(j).unwrap_or(0));
if let Some(v2) = u.checked_sub(v).and_then(|v1| v1.checked_sub(k)) {
// no borrow is needed. u - v - k can be computed as-is
let t = v2;
k = 0;
t
} else {
// borrow is needed. Add a `B` to u, before subtracting.
// PROOF: addition: `u + B < 2*B`, thus can fit in double.
// PROOF: subtraction: if `u - v - k < 0`, then `u + B - v - k < B`.
// NOTE: the order of operations is critical to ensure underflow won't happen.
let t = u + B - v - k;
k = 1;
t
}
};
w.set(j, s as Single);
}
if k.is_zero() {
Ok(w)
} else {
Err(w)
}
}
/// Multiplies n-limb number `self` with m-limb number `other`.
///
/// The resulting number will always have `n + m` limbs.
///
/// This function does not strip the output and returns the original allocated `n + m`
/// limbs. The caller may strip the output if desired.
///
/// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4.
pub fn mul(self, other: &Self) -> Self {
let n = self.len();
let m = other.len();
let mut w = Self::with_capacity(m + n);
for j in 0..n {
if self.get(j) == 0 {
// Note: `with_capacity` allocates with 0. Explicitly set j + m to zero if
// otherwise.
continue;
}
let mut k = 0;
for i in 0..m {
// PROOF: (B1) × (B1) + (B1) + (B1) = B^2 1 < B^2. addition is safe.
let t = mul_single(self.get(j), other.get(i)) +
Double::from(w.get(i + j)) +
Double::from(k);
w.set(i + j, (t % B) as Single);
// PROOF: (B^2 - 1) / B < B. conversion is safe.
k = (t / B) as Single;
}
w.set(j + m, k);
}
w
}
/// Divides `self` by a single limb `other`. This can be used in cases where the original
/// division cannot work due to the divisor (`other`) being just one limb.
///
/// Invariant: `other` cannot be zero.
pub fn div_unit(self, mut other: Single) -> Self {
other = other.max(1);
let n = self.len();
let mut out = Self::with_capacity(n);
let mut r: Single = 0;
// PROOF: (B-1) * B + (B-1) still fits in double
let with_r = |x: Single, r: Single| Double::from(r) * B + Double::from(x);
for d in (0..n).rev() {
let (q, rr) = div_single(with_r(self.get(d), r), other);
out.set(d, q as Single);
r = rr;
}
out
}
/// Divides an `n + m` limb self by a `n` limb `other`. The result is a `m + 1` limb
/// quotient and a `n` limb remainder, if enabled by passing `true` in `rem` argument, both
/// in the form of an option's `Ok`.
///
/// - requires `other` to be stripped and have no leading zeros.
/// - requires `self` to be stripped and have no leading zeros.
/// - requires `other` to have at least two limbs.
/// - requires `self` to have a greater length compared to `other`.
///
/// All arguments are examined without being stripped for the above conditions. If any of
/// the above fails, `None` is returned.`
///
/// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4.
pub fn div(self, other: &Self, rem: bool) -> Option<(Self, Self)> {
if other.len() <= 1 || other.msb() == 0 || self.msb() == 0 || self.len() <= other.len() {
return None;
}
let n = other.len();
let m = self.len() - n;
let mut q = Self::with_capacity(m + 1);
let mut r = Self::with_capacity(n);
// PROOF: 0 <= normalizer_bits < SHIFT 0 <= normalizer < B. all conversions are
// safe.
let normalizer_bits = other.msb().leading_zeros() as Single;
let normalizer = 2_u32.pow(normalizer_bits as u32) as Single;
// step D1.
let mut self_norm = self.mul(&Self::from(normalizer));
let mut other_norm = other.clone().mul(&Self::from(normalizer));
// defensive only; the mul implementation should always create this.
self_norm.lpad(n + m + 1);
other_norm.lstrip();
// step D2.
for j in (0..=m).rev() {
// step D3.0 Find an estimate of q[j], named qhat.
let (qhat, rhat) = {
// PROOF: this always fits into `Double`. In the context of Single = u8, and
// Double = u16, think of 255 * 256 + 255 which is just u16::MAX.
let dividend =
Double::from(self_norm.get(j + n)) * B + Double::from(self_norm.get(j + n - 1));
let divisor = other_norm.get(n - 1);
div_single(dividend, divisor)
};
// D3.1 test qhat
// replace qhat and rhat with RefCells. This helps share state with the closure
let qhat = RefCell::new(qhat);
let rhat = RefCell::new(Double::from(rhat));
let test = || {
// decrease qhat if it is bigger than the base (B)
let qhat_local = *qhat.borrow();
let rhat_local = *rhat.borrow();
let predicate_1 = qhat_local >= B;
let predicate_2 = {
let lhs = qhat_local * Double::from(other_norm.get(n - 2));
let rhs = B * rhat_local + Double::from(self_norm.get(j + n - 2));
lhs > rhs
};
if predicate_1 || predicate_2 {
*qhat.borrow_mut() -= 1;
*rhat.borrow_mut() += Double::from(other_norm.get(n - 1));
true
} else {
false
}
};
test();
while (*rhat.borrow() as Double) < B {
if !test() {
break;
}
}
let qhat = qhat.into_inner();
// we don't need rhat anymore. just let it go out of scope when it does.
// step D4
let lhs = Self { digits: (j..=j + n).rev().map(|d| self_norm.get(d)).collect() };
let rhs = other_norm.clone().mul(&Self::from(qhat));
let maybe_sub = lhs.sub(&rhs);
let mut negative = false;
let sub = match maybe_sub {
Ok(t) => t,
Err(t) => {
negative = true;
t
},
};
(j..=j + n).for_each(|d| {
self_norm.set(d, sub.get(d - j));
});
// step D5
// PROOF: the `test()` specifically decreases qhat until it is below `B`. conversion
// is safe.
q.set(j, qhat as Single);
// step D6: add back if negative happened.
if negative {
q.set(j, q.get(j) - 1);
let u = Self { digits: (j..=j + n).rev().map(|d| self_norm.get(d)).collect() };
let r = other_norm.clone().add(&u);
(j..=j + n).rev().for_each(|d| {
self_norm.set(d, r.get(d - j));
})
}
}
// if requested, calculate remainder.
if rem {
// undo the normalization.
if normalizer_bits > 0 {
let s = SHIFT as u32;
let nb = normalizer_bits;
for d in 0..n - 1 {
let v =
(self_norm.get(d) >> nb) | self_norm.get(d + 1).overflowing_shl(s - nb).0;
r.set(d, v);
}
r.set(n - 1, self_norm.get(n - 1) >> normalizer_bits);
} else {
r = self_norm;
}
}
Some((q, r))
}
}
impl core::fmt::Debug for BigUint {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"BigUint {{ {:?} ({:?})}}",
self.digits,
u128::try_from(self.clone()).unwrap_or(0),
)
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Ok(())
}
}
impl PartialEq for BigUint {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl Eq for BigUint {}
impl Ord for BigUint {
fn cmp(&self, other: &Self) -> Ordering {
let lhs_first = self.digits.iter().position(|&e| e != 0);
let rhs_first = other.digits.iter().position(|&e| e != 0);
match (lhs_first, rhs_first) {
// edge cases that should not happen. This basically means that one or both were
// zero.
(None, None) => Ordering::Equal,
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(Some(lhs_idx), Some(rhs_idx)) => {
let lhs = &self.digits[lhs_idx..];
let rhs = &other.digits[rhs_idx..];
let len_cmp = lhs.len().cmp(&rhs.len());
match len_cmp {
Ordering::Equal => lhs.cmp(rhs),
_ => len_cmp,
}
},
}
}
}
impl PartialOrd for BigUint {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl ops::Add for BigUint {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
self.add(&rhs)
}
}
impl ops::Sub for BigUint {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
self.sub(&rhs).unwrap_or_else(|e| e)
}
}
impl ops::Mul for BigUint {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
self.mul(&rhs)
}
}
impl Zero for BigUint {
fn zero() -> Self {
Self { digits: vec![Zero::zero()] }
}
fn is_zero(&self) -> bool {
self.digits.iter().all(|d| d.is_zero())
}
}
impl One for BigUint {
fn one() -> Self {
Self { digits: vec![Single::one()] }
}
}
macro_rules! impl_try_from_number_for {
($([$type:ty, $len:expr]),+) => {
$(
impl TryFrom<BigUint> for $type {
type Error = &'static str;
fn try_from(mut value: BigUint) -> Result<$type, Self::Error> {
value.lstrip();
let error_message = concat!("cannot fit a number into ", stringify!($type));
if value.len() * SHIFT > $len {
Err(error_message)
} else {
let mut acc: $type = Zero::zero();
for (i, d) in value.digits.iter().rev().cloned().enumerate() {
let d: $type = d.into();
acc += d << (SHIFT * i);
}
Ok(acc)
}
}
}
)*
};
}
// can only be implemented for sizes bigger than two limb.
impl_try_from_number_for!([u128, 128], [u64, 64]);
macro_rules! impl_from_for_smaller_than_word {
($($type:ty),+) => {
$(impl From<$type> for BigUint {
fn from(a: $type) -> Self {
Self { digits: vec! [a.into()] }
}
})*
}
}
impl_from_for_smaller_than_word!(u8, u16, u32);
impl From<u64> for BigUint {
fn from(a: Double) -> Self {
let (ah, al) = split(a);
Self { digits: vec![ah, al] }
}
}
impl From<u128> for BigUint {
fn from(a: u128) -> Self {
crate::helpers_128bit::to_big_uint(a)
}
}
#[cfg(test)]
pub mod tests {
use super::*;
fn with_limbs(n: usize) -> BigUint {
BigUint { digits: vec![1; n] }
}
#[test]
fn split_works() {
let a = SHIFT / 2;
let b = SHIFT * 3 / 2;
let num: Double = (1 << a) | (1 << b);
assert_eq!(num, 0x_0001_0000_0001_0000);
assert_eq!(split(num), (1 << a, 1 << a));
let a = SHIFT / 2 + 4;
let b = SHIFT / 2 - 4;
let num: Double = (1 << (SHIFT + a)) | (1 << b);
assert_eq!(num, 0x_0010_0000_0000_1000);
assert_eq!(split(num), (1 << a, 1 << b));
}
#[test]
fn strip_works() {
let mut a = BigUint::from_limbs(&[0, 1, 0]);
a.lstrip();
assert_eq!(a.digits, vec![1, 0]);
let mut a = BigUint::from_limbs(&[0, 0, 1]);
a.lstrip();
assert_eq!(a.digits, vec![1]);
let mut a = BigUint::from_limbs(&[0, 0]);
a.lstrip();
assert_eq!(a.digits, vec![0]);
let mut a = BigUint::from_limbs(&[0, 0, 0]);
a.lstrip();
assert_eq!(a.digits, vec![0]);
}
#[test]
fn lpad_works() {
let mut a = BigUint::from_limbs(&[0, 1, 0]);
a.lpad(2);
assert_eq!(a.digits, vec![0, 1, 0]);
let mut a = BigUint::from_limbs(&[0, 1, 0]);
a.lpad(3);
assert_eq!(a.digits, vec![0, 1, 0]);
let mut a = BigUint::from_limbs(&[0, 1, 0]);
a.lpad(4);
assert_eq!(a.digits, vec![0, 0, 1, 0]);
}
#[test]
fn equality_works() {
assert_eq!(BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, true);
assert_eq!(BigUint { digits: vec![3, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, false);
assert_eq!(BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, true);
}
#[test]
fn ordering_works() {
assert!(BigUint { digits: vec![0] } < BigUint { digits: vec![1] });
assert!(BigUint { digits: vec![0] } == BigUint { digits: vec![0] });
assert!(BigUint { digits: vec![] } == BigUint { digits: vec![0] });
assert!(BigUint { digits: vec![] } == BigUint { digits: vec![] });
assert!(BigUint { digits: vec![] } < BigUint { digits: vec![1] });
assert!(BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] });
assert!(BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] });
assert!(BigUint { digits: vec![1, 2, 4] } > BigUint { digits: vec![1, 2, 3] });
assert!(BigUint { digits: vec![0, 1, 2, 4] } > BigUint { digits: vec![1, 2, 3] });
assert!(BigUint { digits: vec![1, 2, 1, 0] } > BigUint { digits: vec![1, 2, 3] });
assert!(BigUint { digits: vec![0, 1, 2, 1] } < BigUint { digits: vec![1, 2, 3] });
}
#[test]
fn can_try_build_numbers_from_types() {
assert_eq!(u64::try_from(with_limbs(1)).unwrap(), 1);
assert_eq!(u64::try_from(with_limbs(2)).unwrap(), u32::MAX as u64 + 2);
assert_eq!(u64::try_from(with_limbs(3)).unwrap_err(), "cannot fit a number into u64");
assert_eq!(u128::try_from(with_limbs(3)).unwrap(), u32::MAX as u128 + u64::MAX as u128 + 3);
}
#[test]
fn zero_works() {
assert_eq!(BigUint::zero(), BigUint { digits: vec![0] });
assert_eq!(BigUint { digits: vec![0, 1, 0] }.is_zero(), false);
assert_eq!(BigUint { digits: vec![0, 0, 0] }.is_zero(), true);
let a = BigUint::zero();
let b = BigUint::zero();
let c = a * b;
assert_eq!(c.digits, vec![0, 0]);
}
#[test]
fn sub_negative_works() {
assert_eq!(
BigUint::from(10 as Single).sub(&BigUint::from(5 as Single)).unwrap(),
BigUint::from(5 as Single)
);
assert_eq!(
BigUint::from(10 as Single).sub(&BigUint::from(10 as Single)).unwrap(),
BigUint::from(0 as Single)
);
assert_eq!(
BigUint::from(10 as Single).sub(&BigUint::from(13 as Single)).unwrap_err(),
BigUint::from((B - 3) as Single),
);
}
#[test]
fn mul_always_appends_one_digit() {
let a = BigUint::from(10 as Single);
let b = BigUint::from(4 as Single);
assert_eq!(a.len(), 1);
assert_eq!(b.len(), 1);
let n = a.mul(&b);
assert_eq!(n.len(), 2);
assert_eq!(n.digits, vec![0, 40]);
}
#[test]
fn div_conditions_work() {
let a = BigUint { digits: vec![2] };
let b = BigUint { digits: vec![1, 2] };
let c = BigUint { digits: vec![1, 1, 2] };
let d = BigUint { digits: vec![0, 2] };
let e = BigUint { digits: vec![0, 1, 1, 2] };
let f = BigUint { digits: vec![7, 8] };
assert!(a.clone().div(&b, true).is_none());
assert!(c.clone().div(&a, true).is_none());
assert!(c.clone().div(&d, true).is_none());
assert!(e.clone().div(&a, true).is_none());
assert!(f.clone().div(&b, true).is_none());
assert!(c.clone().div(&b, true).is_some());
}
#[test]
fn div_unit_works() {
let a = BigUint { digits: vec![100] };
let b = BigUint { digits: vec![1, 100] };
let c = BigUint { digits: vec![14, 28, 100] };
assert_eq!(a.clone().div_unit(1), a);
assert_eq!(a.clone().div_unit(0), a);
assert_eq!(a.clone().div_unit(2), BigUint::from(50 as Single));
assert_eq!(a.clone().div_unit(7), BigUint::from(14 as Single));
assert_eq!(b.clone().div_unit(1), b);
assert_eq!(b.clone().div_unit(0), b);
assert_eq!(b.clone().div_unit(2), BigUint::from(((B + 100) / 2) as Single));
assert_eq!(b.clone().div_unit(7), BigUint::from(((B + 100) / 7) as Single));
assert_eq!(c.clone().div_unit(1), c);
assert_eq!(c.clone().div_unit(0), c);
assert_eq!(c.clone().div_unit(2), BigUint { digits: vec![7, 14, 50] });
assert_eq!(c.clone().div_unit(7), BigUint { digits: vec![2, 4, 14] });
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,308 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// Some code is based upon Derek Dreery's IntegerSquareRoot impl, used under license.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Some helper functions to work with 128bit numbers. Note that the functionality provided here is
//! only sensible to use with 128bit numbers because for smaller sizes, you can always rely on
//! assumptions of a bigger type (u128) being available, or simply create a per-thing and use the
//! multiplication implementation provided there.
use crate::{biguint, Rounding};
use core::cmp::{max, min};
/// Helper gcd function used in Rational128 implementation.
pub fn gcd(a: u128, b: u128) -> u128 {
match ((a, b), (a & 1, b & 1)) {
((x, y), _) if x == y => y,
((0, x), _) | ((x, 0), _) => x,
((x, y), (0, 1)) | ((y, x), (1, 0)) => gcd(x >> 1, y),
((x, y), (0, 0)) => gcd(x >> 1, y >> 1) << 1,
((x, y), (1, 1)) => {
let (x, y) = (min(x, y), max(x, y));
gcd((y - x) >> 1, x)
},
_ => unreachable!(),
}
}
/// split a u128 into two u64 limbs
pub fn split(a: u128) -> (u64, u64) {
let al = a as u64;
let ah = (a >> 64) as u64;
(ah, al)
}
/// Convert a u128 to a u32 based biguint.
pub fn to_big_uint(x: u128) -> biguint::BigUint {
let (xh, xl) = split(x);
let (xhh, xhl) = biguint::split(xh);
let (xlh, xll) = biguint::split(xl);
let mut n = biguint::BigUint::from_limbs(&[xhh, xhl, xlh, xll]);
n.lstrip();
n
}
mod double128 {
// Inspired by: https://medium.com/wicketh/mathemagic-512-bit-division-in-solidity-afa55870a65
/// Returns the least significant 64 bits of a
const fn low_64(a: u128) -> u128 {
a & ((1 << 64) - 1)
}
/// Returns the most significant 64 bits of a
const fn high_64(a: u128) -> u128 {
a >> 64
}
/// Returns 2^128 - a (two's complement)
const fn neg128(a: u128) -> u128 {
(!a).wrapping_add(1)
}
/// Returns 2^128 / a
const fn div128(a: u128) -> u128 {
(neg128(a) / a).wrapping_add(1)
}
/// Returns 2^128 % a
const fn mod128(a: u128) -> u128 {
neg128(a) % a
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Double128 {
high: u128,
low: u128,
}
impl Double128 {
pub const fn try_into_u128(self) -> Result<u128, ()> {
match self.high {
0 => Ok(self.low),
_ => Err(()),
}
}
pub const fn zero() -> Self {
Self { high: 0, low: 0 }
}
/// Return a `Double128` value representing the `scaled_value << 64`.
///
/// This means the lower half of the `high` component will be equal to the upper 64-bits of
/// `scaled_value` (in the lower positions) and the upper half of the `low` component will
/// be equal to the lower 64-bits of `scaled_value`.
pub const fn left_shift_64(scaled_value: u128) -> Self {
Self { high: scaled_value >> 64, low: scaled_value << 64 }
}
/// Construct a value from the upper 128 bits only, with the lower being zeroed.
pub const fn from_low(low: u128) -> Self {
Self { high: 0, low }
}
/// Returns the same value ignoring anything in the high 128-bits.
pub const fn low_part(self) -> Self {
Self { high: 0, ..self }
}
/// Returns a*b (in 256 bits)
pub const fn product_of(a: u128, b: u128) -> Self {
// Split a and b into hi and lo 64-bit parts
let (a_low, a_high) = (low_64(a), high_64(a));
let (b_low, b_high) = (low_64(b), high_64(b));
// a = (a_low + a_high << 64); b = (b_low + b_high << 64);
// ergo a*b = (a_low + a_high << 64)(b_low + b_high << 64)
// = a_low * b_low
// + a_low * b_high << 64
// + a_high << 64 * b_low
// + a_high << 64 * b_high << 64
// assuming:
// f = a_low * b_low
// o = a_low * b_high
// i = a_high * b_low
// l = a_high * b_high
// then:
// a*b = (o+i) << 64 + f + l << 128
let (f, o, i, l) = (a_low * b_low, a_low * b_high, a_high * b_low, a_high * b_high);
let fl = Self { high: l, low: f };
let i = Self::left_shift_64(i);
let o = Self::left_shift_64(o);
fl.add(i).add(o)
}
pub const fn add(self, b: Self) -> Self {
let (low, overflow) = self.low.overflowing_add(b.low);
let carry = overflow as u128; // 1 if true, 0 if false.
let high = self.high.wrapping_add(b.high).wrapping_add(carry as u128);
Double128 { high, low }
}
pub const fn div(mut self, rhs: u128) -> (Self, u128) {
if rhs == 1 {
return (self, 0);
}
// (self === a; rhs === b)
// Calculate a / b
// = (a_high << 128 + a_low) / b
// let (q, r) = (div128(b), mod128(b));
// = (a_low * (q * b + r)) + a_high) / b
// = (a_low * q * b + a_low * r + a_high)/b
// = (a_low * r + a_high) / b + a_low * q
let (q, r) = (div128(rhs), mod128(rhs));
// x = current result
// a = next number
let mut x = Self::zero();
while self.high != 0 {
// x += a.low * q
x = x.add(Self::product_of(self.high, q));
// a = a.low * r + a.high
self = Self::product_of(self.high, r).add(self.low_part());
}
(x.add(Self::from_low(self.low / rhs)), self.low % rhs)
}
}
}
/// Returns `a * b / c` (wrapping to 128 bits) or `None` in the case of
/// overflow.
pub const fn multiply_by_rational_with_rounding(
a: u128,
b: u128,
c: u128,
r: Rounding,
) -> Option<u128> {
use double128::Double128;
if c == 0 {
return None;
}
let (result, remainder) = Double128::product_of(a, b).div(c);
let mut result: u128 = match result.try_into_u128() {
Ok(v) => v,
Err(_) => return None,
};
if match r {
Rounding::Up => remainder > 0,
// cannot be `(c + 1) / 2` since `c` might be `max_value` and overflow.
Rounding::NearestPrefUp => remainder >= c / 2 + c % 2,
Rounding::NearestPrefDown => remainder > c / 2,
Rounding::Down => false,
} {
result = match result.checked_add(1) {
Some(v) => v,
None => return None,
};
}
Some(result)
}
pub const fn sqrt(mut n: u128) -> u128 {
// Modified from https://github.com/derekdreery/integer-sqrt-rs (Apache/MIT).
if n == 0 {
return 0;
}
// Compute bit, the largest power of 4 <= n
let max_shift: u32 = 0u128.leading_zeros() - 1;
let shift: u32 = (max_shift - n.leading_zeros()) & !1;
let mut bit = 1u128 << shift;
// Algorithm based on the implementation in:
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)
// Note that result/bit are logically unsigned (even if T is signed).
let mut result = 0u128;
while bit != 0 {
if n >= result + bit {
n -= result + bit;
result = (result >> 1) + bit;
} else {
result = result >> 1;
}
bit = bit >> 2;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use codec::{Decode, Encode};
use multiply_by_rational_with_rounding as mulrat;
use Rounding::*;
const MAX: u128 = u128::max_value();
#[test]
fn rational_multiply_basic_rounding_works() {
assert_eq!(mulrat(1, 1, 1, Up), Some(1));
assert_eq!(mulrat(3, 1, 3, Up), Some(1));
assert_eq!(mulrat(1, 1, 3, Up), Some(1));
assert_eq!(mulrat(1, 2, 3, Down), Some(0));
assert_eq!(mulrat(1, 1, 3, NearestPrefDown), Some(0));
assert_eq!(mulrat(1, 1, 2, NearestPrefDown), Some(0));
assert_eq!(mulrat(1, 2, 3, NearestPrefDown), Some(1));
assert_eq!(mulrat(1, 1, 3, NearestPrefUp), Some(0));
assert_eq!(mulrat(1, 1, 2, NearestPrefUp), Some(1));
assert_eq!(mulrat(1, 2, 3, NearestPrefUp), Some(1));
}
#[test]
fn rational_multiply_big_number_works() {
assert_eq!(mulrat(MAX, MAX - 1, MAX, Down), Some(MAX - 1));
assert_eq!(mulrat(MAX, 1, MAX, Down), Some(1));
assert_eq!(mulrat(MAX, MAX - 1, MAX, Up), Some(MAX - 1));
assert_eq!(mulrat(MAX, 1, MAX, Up), Some(1));
assert_eq!(mulrat(1, MAX - 1, MAX, Down), Some(0));
assert_eq!(mulrat(1, 1, MAX, Up), Some(1));
assert_eq!(mulrat(1, MAX / 2, MAX, NearestPrefDown), Some(0));
assert_eq!(mulrat(1, MAX / 2 + 1, MAX, NearestPrefDown), Some(1));
assert_eq!(mulrat(1, MAX / 2, MAX, NearestPrefUp), Some(0));
assert_eq!(mulrat(1, MAX / 2 + 1, MAX, NearestPrefUp), Some(1));
}
#[test]
fn sqrt_works() {
for i in 0..100_000u32 {
let a = sqrt(random_u128(i));
assert_eq!(sqrt(a * a), a);
}
}
fn random_u128(seed: u32) -> u128 {
u128::decode(&mut &seed.using_encoded(pezsp_crypto_hashing::twox_128)[..]).unwrap_or(0)
}
#[test]
fn op_checked_rounded_div_works() {
for i in 0..100_000u32 {
let a = random_u128(i);
let b = random_u128(i + (1 << 30));
let c = random_u128(i + (1 << 31));
let x = mulrat(a, b, c, NearestPrefDown);
let y = multiply_by_rational_with_rounding(a, b, c, Rounding::NearestPrefDown);
assert_eq!(x.is_some(), y.is_some());
let x = x.unwrap_or(0);
let y = y.unwrap_or(0);
let d = x.max(y) - x.min(y);
assert_eq!(d, 0);
}
}
}
+586
View File
@@ -0,0 +1,586 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Minimal fixed point arithmetic primitives and types for runtime.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
/// Copied from `sp-runtime` and documented there.
#[macro_export]
macro_rules! assert_eq_error_rate {
($x:expr, $y:expr, $error:expr $(,)?) => {
assert!(
($x) >= (($y) - ($error)) && ($x) <= (($y) + ($error)),
"{:?} != {:?} (with error rate {:?})",
$x,
$y,
$error,
);
};
}
pub mod biguint;
pub mod fixed_point;
pub mod helpers_128bit;
pub mod per_things;
pub mod rational;
pub mod traits;
pub use fixed_point::{
FixedI128, FixedI64, FixedPointNumber, FixedPointOperand, FixedU128, FixedU64,
};
pub use per_things::{
InnerOf, MultiplyArg, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, RationalArg,
ReciprocalArg, Rounding, SignedRounding, UpperOf,
};
pub use rational::{MultiplyRational, Rational128, RationalInfinite};
use alloc::vec::Vec;
use core::{cmp::Ordering, fmt::Debug};
use traits::{BaseArithmetic, One, SaturatedConversion, Unsigned, Zero};
use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Arithmetic errors.
#[derive(
Eq,
PartialEq,
Clone,
Copy,
Encode,
Decode,
DecodeWithMemTracking,
Debug,
TypeInfo,
MaxEncodedLen,
)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ArithmeticError {
/// Underflow.
Underflow,
/// Overflow.
Overflow,
/// Division by zero.
DivisionByZero,
}
impl From<ArithmeticError> for &'static str {
fn from(e: ArithmeticError) -> &'static str {
match e {
ArithmeticError::Underflow => "An underflow would occur",
ArithmeticError::Overflow => "An overflow would occur",
ArithmeticError::DivisionByZero => "Division by zero",
}
}
}
/// Trait for comparing two numbers with an threshold.
///
/// Returns:
/// - `Ordering::Greater` if `self` is greater than `other + threshold`.
/// - `Ordering::Less` if `self` is less than `other - threshold`.
/// - `Ordering::Equal` otherwise.
pub trait ThresholdOrd<T> {
/// Compare if `self` is `threshold` greater or less than `other`.
fn tcmp(&self, other: &T, threshold: T) -> Ordering;
}
impl<T> ThresholdOrd<T> for T
where
T: Ord + PartialOrd + Copy + Clone + traits::Zero + traits::Saturating,
{
fn tcmp(&self, other: &T, threshold: T) -> Ordering {
// early exit.
if threshold.is_zero() {
return self.cmp(other);
}
let upper_bound = other.saturating_add(threshold);
let lower_bound = other.saturating_sub(threshold);
if upper_bound <= lower_bound {
// defensive only. Can never happen.
self.cmp(other)
} else {
// upper_bound is guaranteed now to be bigger than lower.
match (self.cmp(&lower_bound), self.cmp(&upper_bound)) {
(Ordering::Greater, Ordering::Greater) => Ordering::Greater,
(Ordering::Less, Ordering::Less) => Ordering::Less,
_ => Ordering::Equal,
}
}
}
}
/// A collection-like object that is made of values of type `T` and can normalize its individual
/// values around a centric point.
///
/// Note that the order of items in the collection may affect the result.
pub trait Normalizable<T> {
/// Normalize self around `targeted_sum`.
///
/// Only returns `Ok` if the new sum of results is guaranteed to be equal to `targeted_sum`.
/// Else, returns an error explaining why it failed to do so.
fn normalize(&self, targeted_sum: T) -> Result<Vec<T>, &'static str>;
}
macro_rules! impl_normalize_for_numeric {
($($numeric:ty),*) => {
$(
impl Normalizable<$numeric> for Vec<$numeric> {
fn normalize(&self, targeted_sum: $numeric) -> Result<Vec<$numeric>, &'static str> {
normalize(self.as_ref(), targeted_sum)
}
}
)*
};
}
impl_normalize_for_numeric!(u8, u16, u32, u64, u128);
impl<P: PerThing> Normalizable<P> for Vec<P> {
fn normalize(&self, targeted_sum: P) -> Result<Vec<P>, &'static str> {
let uppers = self.iter().map(|p| <UpperOf<P>>::from(p.deconstruct())).collect::<Vec<_>>();
let normalized =
normalize(uppers.as_ref(), <UpperOf<P>>::from(targeted_sum.deconstruct()))?;
Ok(normalized
.into_iter()
.map(|i: UpperOf<P>| P::from_parts(i.saturated_into::<P::Inner>()))
.collect())
}
}
/// Normalize `input` so that the sum of all elements reaches `targeted_sum`.
///
/// This implementation is currently in a balanced position between being performant and accurate.
///
/// 1. We prefer storing original indices, and sorting the `input` only once. This will save the
/// cost of sorting per round at the cost of a little bit of memory.
/// 2. The granularity of increment/decrements is determined by the number of elements in `input`
/// and their sum difference with `targeted_sum`, namely `diff = diff(sum(input), target_sum)`.
/// This value is then distributed into `per_round = diff / input.len()` and `leftover = diff %
/// round`. First, per_round is applied to all elements of input, and then we move to leftover,
/// in which case we add/subtract 1 by 1 until `leftover` is depleted.
///
/// When the sum is less than the target, the above approach always holds. In this case, then each
/// individual element is also less than target. Thus, by adding `per_round` to each item, neither
/// of them can overflow the numeric bound of `T`. In fact, neither of the can go beyond
/// `target_sum`*.
///
/// If sum is more than target, there is small twist. The subtraction of `per_round`
/// form each element might go below zero. In this case, we saturate and add the error to the
/// `leftover` value. This ensures that the result will always stay accurate, yet it might cause the
/// execution to become increasingly slow, since leftovers are applied one by one.
///
/// All in all, the complicated case above is rare to happen in most use cases within this repo ,
/// hence we opt for it due to its simplicity.
///
/// This function will return an error is if length of `input` cannot fit in `T`, or if `sum(input)`
/// cannot fit inside `T`.
///
/// * This proof is used in the implementation as well.
pub fn normalize<T>(input: &[T], targeted_sum: T) -> Result<Vec<T>, &'static str>
where
T: Clone + Copy + Ord + BaseArithmetic + Unsigned + Debug,
{
// compute sum and return error if failed.
let mut sum = T::zero();
for t in input.iter() {
sum = sum.checked_add(t).ok_or("sum of input cannot fit in `T`")?;
}
// convert count and return error if failed.
let count = input.len();
let count_t: T = count.try_into().map_err(|_| "length of `inputs` cannot fit in `T`")?;
// Nothing to do here.
if count.is_zero() {
return Ok(Vec::<T>::new());
}
let diff = targeted_sum.max(sum) - targeted_sum.min(sum);
if diff.is_zero() {
return Ok(input.to_vec());
}
let needs_bump = targeted_sum > sum;
let per_round = diff / count_t;
let mut leftover = diff % count_t;
// sort output once based on diff. This will require more data transfer and saving original
// index, but we sort only twice instead: once now and once at the very end.
let mut output_with_idx = input.iter().cloned().enumerate().collect::<Vec<(usize, T)>>();
output_with_idx.sort_by_key(|x| x.1);
if needs_bump {
// must increase the values a bit. Bump from the min element. Index of minimum is now zero
// because we did a sort. If at any point the min goes greater or equal the `max_threshold`,
// we move to the next minimum.
let mut min_index = 0;
// at this threshold we move to next index.
let threshold = targeted_sum / count_t;
if !per_round.is_zero() {
for _ in 0..count {
output_with_idx[min_index].1 = output_with_idx[min_index]
.1
.checked_add(&per_round)
.expect("Proof provided in the module doc; qed.");
if output_with_idx[min_index].1 >= threshold {
min_index += 1;
min_index %= count;
}
}
}
// continue with the previous min_index
while !leftover.is_zero() {
output_with_idx[min_index].1 = output_with_idx[min_index]
.1
.checked_add(&T::one())
.expect("Proof provided in the module doc; qed.");
if output_with_idx[min_index].1 >= threshold {
min_index += 1;
min_index %= count;
}
leftover -= One::one();
}
} else {
// must decrease the stakes a bit. decrement from the max element. index of maximum is now
// last. if at any point the max goes less or equal the `min_threshold`, we move to the next
// maximum.
let mut max_index = count - 1;
// at this threshold we move to next index.
let threshold = output_with_idx
.first()
.expect("length of input is greater than zero; it must have a first; qed")
.1;
if !per_round.is_zero() {
for _ in 0..count {
output_with_idx[max_index].1 =
output_with_idx[max_index].1.checked_sub(&per_round).unwrap_or_else(|| {
let remainder = per_round - output_with_idx[max_index].1;
leftover += remainder;
output_with_idx[max_index].1.saturating_sub(per_round)
});
if output_with_idx[max_index].1 <= threshold {
max_index = max_index.checked_sub(1).unwrap_or(count - 1);
}
}
}
// continue with the previous max_index
while !leftover.is_zero() {
if let Some(next) = output_with_idx[max_index].1.checked_sub(&One::one()) {
output_with_idx[max_index].1 = next;
if output_with_idx[max_index].1 <= threshold {
max_index = max_index.checked_sub(1).unwrap_or(count - 1);
}
leftover -= One::one();
} else {
max_index = max_index.checked_sub(1).unwrap_or(count - 1);
}
}
}
debug_assert_eq!(
output_with_idx.iter().fold(T::zero(), |acc, (_, x)| acc + *x),
targeted_sum,
"sum({:?}) != {:?}",
output_with_idx,
targeted_sum
);
// sort again based on the original index.
output_with_idx.sort_by_key(|x| x.0);
Ok(output_with_idx.into_iter().map(|(_, t)| t).collect())
}
#[cfg(test)]
mod normalize_tests {
use super::*;
#[test]
fn work_for_all_types() {
macro_rules! test_for {
($type:ty) => {
assert_eq!(
normalize(vec![8 as $type, 9, 7, 10].as_ref(), 40).unwrap(),
vec![10, 10, 10, 10],
);
};
}
// it should work for all types as long as the length of vector can be converted to T.
test_for!(u128);
test_for!(u64);
test_for!(u32);
test_for!(u16);
test_for!(u8);
}
#[test]
fn fails_on_if_input_sum_large() {
assert!(normalize(vec![1u8; 255].as_ref(), 10).is_ok());
assert_eq!(normalize(vec![1u8; 256].as_ref(), 10), Err("sum of input cannot fit in `T`"));
}
#[test]
fn does_not_fail_on_subtraction_overflow() {
assert_eq!(normalize(vec![1u8, 100, 100].as_ref(), 10).unwrap(), vec![1, 9, 0]);
assert_eq!(normalize(vec![1u8, 8, 9].as_ref(), 1).unwrap(), vec![0, 1, 0]);
}
#[test]
fn works_for_vec() {
assert_eq!(vec![8u32, 9, 7, 10].normalize(40).unwrap(), vec![10u32, 10, 10, 10]);
}
#[test]
fn works_for_per_thing() {
assert_eq!(
vec![Perbill::from_percent(33), Perbill::from_percent(33), Perbill::from_percent(33)]
.normalize(Perbill::one())
.unwrap(),
vec![
Perbill::from_parts(333333334),
Perbill::from_parts(333333333),
Perbill::from_parts(333333333)
]
);
assert_eq!(
vec![Perbill::from_percent(20), Perbill::from_percent(15), Perbill::from_percent(30)]
.normalize(Perbill::one())
.unwrap(),
vec![
Perbill::from_parts(316666668),
Perbill::from_parts(383333332),
Perbill::from_parts(300000000)
]
);
}
#[test]
fn can_work_for_peru16() {
// Peru16 is a rather special case; since inner type is exactly the same as capacity, we
// could have a situation where the sum cannot be calculated in the inner type. Calculating
// using the upper type of the per_thing should assure this to be okay.
assert_eq!(
vec![PerU16::from_percent(40), PerU16::from_percent(40), PerU16::from_percent(40)]
.normalize(PerU16::one())
.unwrap(),
vec![
PerU16::from_parts(21845), // 33%
PerU16::from_parts(21845), // 33%
PerU16::from_parts(21845) // 33%
]
);
}
#[test]
fn normalize_works_all_le() {
assert_eq!(normalize(vec![8u32, 9, 7, 10].as_ref(), 40).unwrap(), vec![10, 10, 10, 10]);
assert_eq!(normalize(vec![7u32, 7, 7, 7].as_ref(), 40).unwrap(), vec![10, 10, 10, 10]);
assert_eq!(normalize(vec![7u32, 7, 7, 10].as_ref(), 40).unwrap(), vec![11, 11, 8, 10]);
assert_eq!(normalize(vec![7u32, 8, 7, 10].as_ref(), 40).unwrap(), vec![11, 8, 11, 10]);
assert_eq!(normalize(vec![7u32, 7, 8, 10].as_ref(), 40).unwrap(), vec![11, 11, 8, 10]);
}
#[test]
fn normalize_works_some_ge() {
assert_eq!(normalize(vec![8u32, 11, 9, 10].as_ref(), 40).unwrap(), vec![10, 11, 9, 10]);
}
#[test]
fn always_inc_min() {
assert_eq!(normalize(vec![10u32, 7, 10, 10].as_ref(), 40).unwrap(), vec![10, 10, 10, 10]);
assert_eq!(normalize(vec![10u32, 10, 7, 10].as_ref(), 40).unwrap(), vec![10, 10, 10, 10]);
assert_eq!(normalize(vec![10u32, 10, 10, 7].as_ref(), 40).unwrap(), vec![10, 10, 10, 10]);
}
#[test]
fn normalize_works_all_ge() {
assert_eq!(normalize(vec![12u32, 11, 13, 10].as_ref(), 40).unwrap(), vec![10, 10, 10, 10]);
assert_eq!(normalize(vec![13u32, 13, 13, 13].as_ref(), 40).unwrap(), vec![10, 10, 10, 10]);
assert_eq!(normalize(vec![13u32, 13, 13, 10].as_ref(), 40).unwrap(), vec![12, 9, 9, 10]);
assert_eq!(normalize(vec![13u32, 12, 13, 10].as_ref(), 40).unwrap(), vec![9, 12, 9, 10]);
assert_eq!(normalize(vec![13u32, 13, 12, 10].as_ref(), 40).unwrap(), vec![9, 9, 12, 10]);
}
}
#[cfg(test)]
mod per_and_fixed_examples {
use super::*;
#[docify::export]
#[test]
fn percent_mult() {
let percent = Percent::from_rational(5u32, 100u32); // aka, 5%
let five_percent_of_100 = percent * 100u32; // 5% of 100 is 5.
assert_eq!(five_percent_of_100, 5)
}
#[docify::export]
#[test]
fn perbill_example() {
let p = Perbill::from_percent(80);
// 800000000 bil, or a representative of 0.800000000.
// Precision is in the billions place.
assert_eq!(p.deconstruct(), 800000000);
}
#[docify::export]
#[test]
fn percent_example() {
let percent = Percent::from_rational(190u32, 400u32);
assert_eq!(percent.deconstruct(), 47);
}
#[docify::export]
#[test]
fn fixed_u64_block_computation_example() {
// Calculate a very rudimentary on-chain price from supply / demand
// Supply: Cores available per block
// Demand: Cores being ordered per block
let price = FixedU64::from_rational(5u128, 10u128);
// 0.5 HEZ per core
assert_eq!(price, FixedU64::from_float(0.5));
// Now, the story has changed - lots of demand means we buy as many cores as there
// available. This also means that price goes up! For the sake of simplicity, we don't care
// about who gets a core - just about our very simple price model
// Calculate a very rudimentary on-chain price from supply / demand
// Supply: Cores available per block
// Demand: Cores being ordered per block
let price = FixedU64::from_rational(19u128, 10u128);
// 1.9 HEZ per core
assert_eq!(price, FixedU64::from_float(1.9));
}
#[docify::export]
#[test]
fn fixed_u64() {
// The difference between this and perthings is perthings operates within the relam of [0,
// 1] In cases where we need > 1, we can used fixed types such as FixedU64
let rational_1 = FixedU64::from_rational(10, 5); //" 200%" aka 2.
let rational_2 = FixedU64::from_rational_with_rounding(5, 10, Rounding::Down); // "50%" aka 0.50...
assert_eq!(rational_1, (2u64).into());
assert_eq!(rational_2.into_perbill(), Perbill::from_float(0.5));
}
#[docify::export]
#[test]
fn fixed_u64_operation_example() {
let rational_1 = FixedU64::from_rational(10, 5); // "200%" aka 2.
let rational_2 = FixedU64::from_rational(8, 5); // "160%" aka 1.6.
let addition = rational_1 + rational_2;
let multiplication = rational_1 * rational_2;
let division = rational_1 / rational_2;
let subtraction = rational_1 - rational_2;
assert_eq!(addition, FixedU64::from_float(3.6));
assert_eq!(multiplication, FixedU64::from_float(3.2));
assert_eq!(division, FixedU64::from_float(1.25));
assert_eq!(subtraction, FixedU64::from_float(0.4));
}
}
#[cfg(test)]
mod threshold_compare_tests {
use super::*;
use crate::traits::Saturating;
use core::cmp::Ordering;
#[test]
fn epsilon_ord_works() {
let b = 115u32;
let e = Perbill::from_percent(10).mul_ceil(b);
// [115 - 11,5 (103,5), 115 + 11,5 (126,5)] is all equal
assert_eq!((103u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((104u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((115u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((120u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((126u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((127u32).tcmp(&b, e), Ordering::Equal);
assert_eq!((128u32).tcmp(&b, e), Ordering::Greater);
assert_eq!((102u32).tcmp(&b, e), Ordering::Less);
}
#[test]
fn epsilon_ord_works_with_small_epc() {
let b = 115u32;
// way less than 1 percent. threshold will be zero. Result should be same as normal ord.
let e = Perbill::from_parts(100) * b;
// [115 - 11,5 (103,5), 115 + 11,5 (126,5)] is all equal
assert_eq!((103u32).tcmp(&b, e), (103u32).cmp(&b));
assert_eq!((104u32).tcmp(&b, e), (104u32).cmp(&b));
assert_eq!((115u32).tcmp(&b, e), (115u32).cmp(&b));
assert_eq!((120u32).tcmp(&b, e), (120u32).cmp(&b));
assert_eq!((126u32).tcmp(&b, e), (126u32).cmp(&b));
assert_eq!((127u32).tcmp(&b, e), (127u32).cmp(&b));
assert_eq!((128u32).tcmp(&b, e), (128u32).cmp(&b));
assert_eq!((102u32).tcmp(&b, e), (102u32).cmp(&b));
}
#[test]
fn peru16_rational_does_not_overflow() {
// A historical example that will panic only for per_thing type that are created with
// maximum capacity of their type, e.g. PerU16.
let _ = PerU16::from_rational(17424870u32, 17424870);
}
#[test]
fn saturating_mul_works() {
assert_eq!(Saturating::saturating_mul(2, i32::MIN), i32::MIN);
assert_eq!(Saturating::saturating_mul(2, i32::MAX), i32::MAX);
}
#[test]
fn saturating_pow_works() {
assert_eq!(Saturating::saturating_pow(i32::MIN, 0), 1);
assert_eq!(Saturating::saturating_pow(i32::MAX, 0), 1);
assert_eq!(Saturating::saturating_pow(i32::MIN, 3), i32::MIN);
assert_eq!(Saturating::saturating_pow(i32::MIN, 2), i32::MAX);
assert_eq!(Saturating::saturating_pow(i32::MAX, 2), i32::MAX);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,583 @@
// This file is part of Bizinikiwi.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{biguint::BigUint, helpers_128bit, Rounding};
use core::cmp::Ordering;
use num_traits::{Bounded, One, Zero};
/// A wrapper for any rational number with infinitely large numerator and denominator.
///
/// This type exists to facilitate `cmp` operation
/// on values like `a/b < c/d` where `a, b, c, d` are all `BigUint`.
#[derive(Clone, Default, Eq)]
pub struct RationalInfinite(BigUint, BigUint);
impl RationalInfinite {
/// Return the numerator reference.
pub fn n(&self) -> &BigUint {
&self.0
}
/// Return the denominator reference.
pub fn d(&self) -> &BigUint {
&self.1
}
/// Build from a raw `n/d`.
pub fn from(n: BigUint, d: BigUint) -> Self {
Self(n, d.max(BigUint::one()))
}
/// Zero.
pub fn zero() -> Self {
Self(BigUint::zero(), BigUint::one())
}
/// One.
pub fn one() -> Self {
Self(BigUint::one(), BigUint::one())
}
}
impl PartialOrd for RationalInfinite {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for RationalInfinite {
fn cmp(&self, other: &Self) -> Ordering {
// handle some edge cases.
if self.d() == other.d() {
self.n().cmp(other.n())
} else if self.d().is_zero() {
Ordering::Greater
} else if other.d().is_zero() {
Ordering::Less
} else {
// (a/b) cmp (c/d) => (a*d) cmp (c*b)
self.n().clone().mul(other.d()).cmp(&other.n().clone().mul(self.d()))
}
}
}
impl PartialEq for RationalInfinite {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl From<Rational128> for RationalInfinite {
fn from(t: Rational128) -> Self {
Self(t.0.into(), t.1.into())
}
}
/// A wrapper for any rational number with a 128 bit numerator and denominator.
#[derive(Clone, Copy, Default, Eq)]
pub struct Rational128(u128, u128);
#[cfg(feature = "std")]
impl core::fmt::Debug for Rational128 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Rational128({} / {} ≈ {:.8})", self.0, self.1, self.0 as f64 / self.1 as f64)
}
}
#[cfg(not(feature = "std"))]
impl core::fmt::Debug for Rational128 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Rational128({} / {})", self.0, self.1)
}
}
impl Rational128 {
/// Zero.
pub fn zero() -> Self {
Self(0, 1)
}
/// One
pub fn one() -> Self {
Self(1, 1)
}
/// If it is zero or not
pub fn is_zero(&self) -> bool {
self.0.is_zero()
}
/// Build from a raw `n/d`.
pub fn from(n: u128, d: u128) -> Self {
Self(n, d.max(1))
}
/// Build from a raw `n/d`. This could lead to / 0 if not properly handled.
pub fn from_unchecked(n: u128, d: u128) -> Self {
Self(n, d)
}
/// Return the numerator.
pub fn n(&self) -> u128 {
self.0
}
/// Return the denominator.
pub fn d(&self) -> u128 {
self.1
}
/// Convert `self` to a similar rational number where denominator is the given `den`.
//
/// This only returns if the result is accurate. `None` is returned if the result cannot be
/// accurately calculated.
pub fn to_den(self, den: u128) -> Option<Self> {
if den == self.1 {
Some(self)
} else {
helpers_128bit::multiply_by_rational_with_rounding(
self.0,
den,
self.1,
Rounding::NearestPrefDown,
)
.map(|n| Self(n, den))
}
}
/// Get the least common divisor of `self` and `other`.
///
/// This only returns if the result is accurate. `None` is returned if the result cannot be
/// accurately calculated.
pub fn lcm(&self, other: &Self) -> Option<u128> {
// this should be tested better: two large numbers that are almost the same.
if self.1 == other.1 {
return Some(self.1);
}
let g = helpers_128bit::gcd(self.1, other.1);
helpers_128bit::multiply_by_rational_with_rounding(
self.1,
other.1,
g,
Rounding::NearestPrefDown,
)
}
/// A saturating add that assumes `self` and `other` have the same denominator.
pub fn lazy_saturating_add(self, other: Self) -> Self {
if other.is_zero() {
self
} else {
Self(self.0.saturating_add(other.0), self.1)
}
}
/// A saturating subtraction that assumes `self` and `other` have the same denominator.
pub fn lazy_saturating_sub(self, other: Self) -> Self {
if other.is_zero() {
self
} else {
Self(self.0.saturating_sub(other.0), self.1)
}
}
/// Addition. Simply tries to unify the denominators and add the numerators.
///
/// Overflow might happen during any of the steps. Error is returned in such cases.
pub fn checked_add(self, other: Self) -> Result<Self, &'static str> {
let lcm = self.lcm(&other).ok_or(0).map_err(|_| "failed to scale to denominator")?;
let self_scaled =
self.to_den(lcm).ok_or(0).map_err(|_| "failed to scale to denominator")?;
let other_scaled =
other.to_den(lcm).ok_or(0).map_err(|_| "failed to scale to denominator")?;
let n = self_scaled
.0
.checked_add(other_scaled.0)
.ok_or("overflow while adding numerators")?;
Ok(Self(n, self_scaled.1))
}
/// Subtraction. Simply tries to unify the denominators and subtract the numerators.
///
/// Overflow might happen during any of the steps. None is returned in such cases.
pub fn checked_sub(self, other: Self) -> Result<Self, &'static str> {
let lcm = self.lcm(&other).ok_or(0).map_err(|_| "failed to scale to denominator")?;
let self_scaled =
self.to_den(lcm).ok_or(0).map_err(|_| "failed to scale to denominator")?;
let other_scaled =
other.to_den(lcm).ok_or(0).map_err(|_| "failed to scale to denominator")?;
let n = self_scaled
.0
.checked_sub(other_scaled.0)
.ok_or("overflow while subtracting numerators")?;
Ok(Self(n, self_scaled.1))
}
}
impl Bounded for Rational128 {
fn min_value() -> Self {
Self(0, 1)
}
fn max_value() -> Self {
Self(Bounded::max_value(), 1)
}
}
impl<T: Into<u128>> From<T> for Rational128 {
fn from(t: T) -> Self {
Self::from(t.into(), 1)
}
}
impl PartialOrd for Rational128 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Rational128 {
fn cmp(&self, other: &Self) -> Ordering {
// handle some edge cases.
if self.1 == other.1 {
self.0.cmp(&other.0)
} else if self.1.is_zero() {
Ordering::Greater
} else if other.1.is_zero() {
Ordering::Less
} else {
// Don't even compute gcd.
let self_n = helpers_128bit::to_big_uint(self.0) * helpers_128bit::to_big_uint(other.1);
let other_n =
helpers_128bit::to_big_uint(other.0) * helpers_128bit::to_big_uint(self.1);
self_n.cmp(&other_n)
}
}
}
impl PartialEq for Rational128 {
fn eq(&self, other: &Self) -> bool {
// handle some edge cases.
if self.1 == other.1 {
self.0.eq(&other.0)
} else {
let self_n = helpers_128bit::to_big_uint(self.0) * helpers_128bit::to_big_uint(other.1);
let other_n =
helpers_128bit::to_big_uint(other.0) * helpers_128bit::to_big_uint(self.1);
self_n.eq(&other_n)
}
}
}
pub trait MultiplyRational: Sized {
fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option<Self>;
}
macro_rules! impl_rrm {
($ulow:ty, $uhi:ty) => {
impl MultiplyRational for $ulow {
fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option<Self> {
if d.is_zero() {
return None;
}
let sn = (self as $uhi) * (n as $uhi);
let mut result = sn / (d as $uhi);
let remainder = (sn % (d as $uhi)) as $ulow;
if match r {
Rounding::Up => remainder > 0,
// cannot be `(d + 1) / 2` since `d` might be `max_value` and overflow.
Rounding::NearestPrefUp => remainder >= d / 2 + d % 2,
Rounding::NearestPrefDown => remainder > d / 2,
Rounding::Down => false,
} {
result = match result.checked_add(1) {
Some(v) => v,
None => return None,
};
}
if result > (<$ulow>::max_value() as $uhi) {
None
} else {
Some(result as $ulow)
}
}
}
};
}
impl_rrm!(u8, u16);
impl_rrm!(u16, u32);
impl_rrm!(u32, u64);
impl_rrm!(u64, u128);
impl MultiplyRational for u128 {
fn multiply_rational(self, n: Self, d: Self, r: Rounding) -> Option<Self> {
crate::helpers_128bit::multiply_by_rational_with_rounding(self, n, d, r)
}
}
#[cfg(test)]
mod tests {
use super::{helpers_128bit::*, *};
use static_assertions::const_assert;
const MAX128: u128 = u128::MAX;
const MAX64: u128 = u64::MAX as u128;
const MAX64_2: u128 = 2 * u64::MAX as u128;
fn r(p: u128, q: u128) -> Rational128 {
Rational128(p, q)
}
fn mul_div(a: u128, b: u128, c: u128) -> u128 {
use primitive_types::U256;
if a.is_zero() {
return Zero::zero();
}
let c = c.max(1);
// e for extended
let ae: U256 = a.into();
let be: U256 = b.into();
let ce: U256 = c.into();
let r = ae * be / ce;
if r > u128::max_value().into() {
a
} else {
r.as_u128()
}
}
#[test]
fn truth_value_function_works() {
assert_eq!(mul_div(2u128.pow(100), 8, 4), 2u128.pow(101));
assert_eq!(mul_div(2u128.pow(100), 4, 8), 2u128.pow(99));
// and it returns a if result cannot fit
assert_eq!(mul_div(MAX128 - 10, 2, 1), MAX128 - 10);
}
#[test]
fn to_denom_works() {
// simple up and down
assert_eq!(r(1, 5).to_den(10), Some(r(2, 10)));
assert_eq!(r(4, 10).to_den(5), Some(r(2, 5)));
// up and down with large numbers
assert_eq!(r(MAX128 - 10, MAX128).to_den(10), Some(r(10, 10)));
assert_eq!(r(MAX128 / 2, MAX128).to_den(10), Some(r(5, 10)));
// large to perbill. This is very well needed for npos-elections.
assert_eq!(r(MAX128 / 2, MAX128).to_den(1000_000_000), Some(r(500_000_000, 1000_000_000)));
// large to large
assert_eq!(r(MAX128 / 2, MAX128).to_den(MAX128 / 2), Some(r(MAX128 / 4, MAX128 / 2)));
}
#[test]
fn gdc_works() {
assert_eq!(gcd(10, 5), 5);
assert_eq!(gcd(7, 22), 1);
}
#[test]
fn lcm_works() {
// simple stuff
assert_eq!(r(3, 10).lcm(&r(4, 15)).unwrap(), 30);
assert_eq!(r(5, 30).lcm(&r(1, 7)).unwrap(), 210);
assert_eq!(r(5, 30).lcm(&r(1, 10)).unwrap(), 30);
// large numbers
assert_eq!(r(1_000_000_000, MAX128).lcm(&r(7_000_000_000, MAX128 - 1)), None,);
assert_eq!(
r(1_000_000_000, MAX64).lcm(&r(7_000_000_000, MAX64 - 1)),
Some(340282366920938463408034375210639556610),
);
const_assert!(340282366920938463408034375210639556610 < MAX128);
const_assert!(340282366920938463408034375210639556610 == MAX64 * (MAX64 - 1));
}
#[test]
fn add_works() {
// works
assert_eq!(r(3, 10).checked_add(r(1, 10)).unwrap(), r(2, 5));
assert_eq!(r(3, 10).checked_add(r(3, 7)).unwrap(), r(51, 70));
// errors
assert_eq!(
r(1, MAX128).checked_add(r(1, MAX128 - 1)),
Err("failed to scale to denominator"),
);
assert_eq!(
r(7, MAX128).checked_add(r(MAX128, MAX128)),
Err("overflow while adding numerators"),
);
assert_eq!(
r(MAX128, MAX128).checked_add(r(MAX128, MAX128)),
Err("overflow while adding numerators"),
);
}
#[test]
fn sub_works() {
// works
assert_eq!(r(3, 10).checked_sub(r(1, 10)).unwrap(), r(1, 5));
assert_eq!(r(6, 10).checked_sub(r(3, 7)).unwrap(), r(12, 70));
// errors
assert_eq!(
r(2, MAX128).checked_sub(r(1, MAX128 - 1)),
Err("failed to scale to denominator"),
);
assert_eq!(
r(7, MAX128).checked_sub(r(MAX128, MAX128)),
Err("overflow while subtracting numerators"),
);
assert_eq!(r(1, 10).checked_sub(r(2, 10)), Err("overflow while subtracting numerators"));
}
#[test]
fn ordering_and_eq_works() {
assert!(r(1, 2) > r(1, 3));
assert!(r(1, 2) > r(2, 6));
assert!(r(1, 2) < r(6, 6));
assert!(r(2, 1) > r(2, 6));
assert!(r(5, 10) == r(1, 2));
assert!(r(1, 2) == r(1, 2));
assert!(r(1, 1490000000000200000) > r(1, 1490000000000200001));
}
#[test]
fn multiply_by_rational_with_rounding_works() {
assert_eq!(multiply_by_rational_with_rounding(7, 2, 3, Rounding::Down).unwrap(), 7 * 2 / 3);
assert_eq!(
multiply_by_rational_with_rounding(7, 20, 30, Rounding::Down).unwrap(),
7 * 2 / 3
);
assert_eq!(
multiply_by_rational_with_rounding(20, 7, 30, Rounding::Down).unwrap(),
7 * 2 / 3
);
assert_eq!(
// MAX128 % 3 == 0
multiply_by_rational_with_rounding(MAX128, 2, 3, Rounding::Down).unwrap(),
MAX128 / 3 * 2,
);
assert_eq!(
// MAX128 % 7 == 3
multiply_by_rational_with_rounding(MAX128, 5, 7, Rounding::Down).unwrap(),
(MAX128 / 7 * 5) + (3 * 5 / 7),
);
assert_eq!(
// MAX128 % 7 == 3
multiply_by_rational_with_rounding(MAX128, 11, 13, Rounding::Down).unwrap(),
(MAX128 / 13 * 11) + (8 * 11 / 13),
);
assert_eq!(
// MAX128 % 1000 == 455
multiply_by_rational_with_rounding(MAX128, 555, 1000, Rounding::Down).unwrap(),
(MAX128 / 1000 * 555) + (455 * 555 / 1000),
);
assert_eq!(
multiply_by_rational_with_rounding(2 * MAX64 - 1, MAX64, MAX64, Rounding::Down)
.unwrap(),
2 * MAX64 - 1
);
assert_eq!(
multiply_by_rational_with_rounding(2 * MAX64 - 1, MAX64 - 1, MAX64, Rounding::Down)
.unwrap(),
2 * MAX64 - 3
);
assert_eq!(
multiply_by_rational_with_rounding(MAX64 + 100, MAX64_2, MAX64_2 / 2, Rounding::Down)
.unwrap(),
(MAX64 + 100) * 2,
);
assert_eq!(
multiply_by_rational_with_rounding(
MAX64 + 100,
MAX64_2 / 100,
MAX64_2 / 200,
Rounding::Down
)
.unwrap(),
(MAX64 + 100) * 2,
);
assert_eq!(
multiply_by_rational_with_rounding(
2u128.pow(66) - 1,
2u128.pow(65) - 1,
2u128.pow(65),
Rounding::Down
)
.unwrap(),
73786976294838206461,
);
assert_eq!(
multiply_by_rational_with_rounding(1_000_000_000, MAX128 / 8, MAX128 / 2, Rounding::Up)
.unwrap(),
250000000
);
assert_eq!(
multiply_by_rational_with_rounding(
29459999999999999988000u128,
1000000000000000000u128,
10000000000000000000u128,
Rounding::Down
)
.unwrap(),
2945999999999999998800u128
);
}
#[test]
fn multiply_by_rational_with_rounding_a_b_are_interchangeable() {
assert_eq!(
multiply_by_rational_with_rounding(10, MAX128, MAX128 / 2, Rounding::NearestPrefDown),
Some(20)
);
assert_eq!(
multiply_by_rational_with_rounding(MAX128, 10, MAX128 / 2, Rounding::NearestPrefDown),
Some(20)
);
}
#[test]
#[ignore]
fn multiply_by_rational_with_rounding_fuzzed_equation() {
assert_eq!(
multiply_by_rational_with_rounding(
154742576605164960401588224,
9223376310179529214,
549756068598,
Rounding::NearestPrefDown
),
Some(2596149632101417846585204209223679)
);
}
}
File diff suppressed because it is too large Load Diff