per_thing_from_rational/
per_thing_from_rational.rs1use fraction::prelude::BigFraction as Fraction;
27use honggfuzz::fuzz;
28use sp_arithmetic::{
29 traits::SaturatedConversion, PerThing, Perbill, Percent, Perquintill, Rounding::*, *,
30};
31
32fn main() {
38 loop {
39 fuzz!(|data: (u128, u128, ArbitraryRounding)| {
40 let (n, d, r) = (data.0.min(data.1), data.0.max(data.1).max(1), data.2);
41
42 check::<PerU16>(n, d, r.0);
43 check::<Percent>(n, d, r.0);
44 check::<Permill>(n, d, r.0);
45 check::<Perbill>(n, d, r.0);
46 check::<Perquintill>(n, d, r.0);
47 })
48 }
49}
50
51fn check<Per: PerThing>(a: u128, b: u128, r: Rounding)
53where
54 Per::Inner: Into<u128>,
55{
56 let approx_ratio = Per::from_rational_with_rounding(a, b, r).unwrap();
57 let approx_parts = Fraction::from(approx_ratio.deconstruct().saturated_into::<u128>());
58
59 let perfect_ratio = if a == 0 && b == 0 {
60 Fraction::from(1)
61 } else {
62 Fraction::from(a) / Fraction::from(b.max(1))
63 };
64 let perfect_parts = round(perfect_ratio * Fraction::from(Per::ACCURACY.into()), r);
65
66 assert_eq!(
67 approx_parts, perfect_parts,
68 "approx_parts: {}, perfect_parts: {}, a: {}, b: {}",
69 approx_parts, perfect_parts, a, b
70 );
71}
72
73fn round(f: Fraction, r: Rounding) -> Fraction {
75 match r {
76 Up => f.ceil(),
77 NearestPrefUp =>
78 if f.fract() < Fraction::from(0.5) {
79 f.floor()
80 } else {
81 f.ceil()
82 },
83 Down => f.floor(),
84 NearestPrefDown =>
85 if f.fract() > Fraction::from(0.5) {
86 f.ceil()
87 } else {
88 f.floor()
89 },
90 }
91}
92
93struct ArbitraryRounding(Rounding);
95impl arbitrary::Arbitrary<'_> for ArbitraryRounding {
96 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
97 Ok(Self(match u.int_in_range(0..=3).unwrap() {
98 0 => Up,
99 1 => NearestPrefUp,
100 2 => Down,
101 3 => NearestPrefDown,
102 _ => unreachable!(),
103 }))
104 }
105}