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:
@@ -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: (B−1) × (B−1) + (B−1) + (B−1) = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user