referrerpolicy=no-referrer-when-downgrade

multiply_by_rational_with_rounding/
multiply_by_rational_with_rounding.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Running
19//! Running this fuzzer can be done with `cargo hfuzz run multiply_by_rational_with_rounding`.
20//! `honggfuzz` CLI options can be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4
21//! threads.
22//!
23//! # Debugging a panic
24//! Once a panic is found, it can be debugged with
25//! `cargo hfuzz run-debug multiply_by_rational_with_rounding
26//! hfuzz_workspace/multiply_by_rational_with_rounding/*.fuzz`.
27//!
28//! # More information
29//! More information about `honggfuzz` can be found
30//! [here](https://docs.rs/honggfuzz/).
31
32use fraction::prelude::BigFraction as Fraction;
33use honggfuzz::fuzz;
34use sp_arithmetic::{MultiplyRational, Rounding, Rounding::*};
35
36/// Tries to demonstrate that `multiply_by_rational_with_rounding` is incorrect.
37fn main() {
38	loop {
39		fuzz!(|data: (u128, u128, u128, ArbitraryRounding)| {
40			let (f, n, d, r) = (data.0, data.1, data.2, data.3 .0);
41
42			check::<u8>(f as u8, n as u8, d as u8, r);
43			check::<u16>(f as u16, n as u16, d as u16, r);
44			check::<u32>(f as u32, n as u32, d as u32, r);
45			check::<u64>(f as u64, n as u64, d as u64, r);
46			check::<u128>(f, n, d, r);
47		})
48	}
49}
50
51fn check<N>(f: N, n: N, d: N, r: Rounding)
52where
53	N: MultiplyRational + Into<u128> + Copy + core::fmt::Debug,
54{
55	let Some(got) = f.multiply_rational(n, d, r) else { return };
56
57	let (ae, be, ce) =
58		(Fraction::from(f.into()), Fraction::from(n.into()), Fraction::from(d.into()));
59	let want = round(ae * be / ce, r);
60
61	assert_eq!(
62		Fraction::from(got.into()),
63		want,
64		"{:?} * {:?} / {:?} = {:?} != {:?}",
65		f,
66		n,
67		d,
68		got,
69		want
70	);
71}
72
73/// Round a `Fraction` according to the given mode.
74fn 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
93/// An [`arbitrary::Arbitrary`] [`Rounding`] mode.
94struct 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}