binius_ip_prover/fracaddcheck/
fraction.rs1use binius_field::Field;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct Fraction<T> {
23 pub num: T,
25 pub den: T,
27}
28
29impl<T> Fraction<T> {
30 pub const fn new(num: T, den: T) -> Self {
32 Self { num, den }
33 }
34
35 pub const fn as_ref(&self) -> Fraction<&T> {
37 Fraction {
38 num: &self.num,
39 den: &self.den,
40 }
41 }
42
43 pub fn map<U>(self, mut f: impl FnMut(T) -> U) -> Fraction<U> {
48 Fraction {
49 num: f(self.num),
50 den: f(self.den),
51 }
52 }
53}
54
55impl<F: Field> Fraction<F> {
56 pub const ZERO: Self = Self {
62 num: F::ZERO,
63 den: F::ONE,
64 };
65}
66
67impl<T> From<(T, T)> for Fraction<T> {
68 fn from((num, den): (T, T)) -> Self {
69 Self { num, den }
70 }
71}
72
73impl<T> From<Fraction<T>> for (T, T) {
74 fn from(Fraction { num, den }: Fraction<T>) -> Self {
75 (num, den)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use binius_field::FieldOps;
82 use binius_math::test_utils::{Packed128b, random_scalars};
83 use rand::prelude::*;
84
85 use super::*;
86
87 type F = <Packed128b as FieldOps>::Scalar;
88
89 #[test]
90 fn zero_fraction_is_the_additive_identity() {
91 let mut rng = StdRng::seed_from_u64(0);
92 let [num, den]: [F; 2] = random_scalars::<F>(&mut rng, 2)
93 .try_into()
94 .expect("two scalars");
95 let f = Fraction::new(num, den);
96
97 let pad = Fraction::<F>::ZERO;
99 let sum = Fraction::new(f.num * pad.den + pad.num * f.den, f.den * pad.den);
100 assert_eq!(sum, f);
101 }
102
103 #[test]
104 fn tuple_conversion_round_trips() {
105 let mut rng = StdRng::seed_from_u64(1);
106 let [num, den]: [F; 2] = random_scalars::<F>(&mut rng, 2)
107 .try_into()
108 .expect("two scalars");
109
110 let tuple: (F, F) = Fraction::new(num, den).into();
111 assert_eq!(tuple, (num, den));
112 assert_eq!(Fraction::from(tuple), Fraction::new(num, den));
113 }
114
115 #[test]
116 fn map_and_as_ref_reach_both_halves() {
117 let f = Fraction::new(vec![1u8, 2], vec![3u8]);
118 assert_eq!(f.as_ref().map(Vec::len), Fraction::new(2, 1));
119 }
120}