binius_circuits/bignum/
big_uint_divide.rs1use binius_core::Word;
5use binius_frontend::{CircuitBuilder, Wire, hints::Hint};
6
7use super::num_biguint_from_u64_limbs;
8
9pub struct BigUintDivideHint;
10
11impl BigUintDivideHint {
12 pub const fn new() -> Self {
13 Self
14 }
15
16 pub fn call(
24 builder: &CircuitBuilder,
25 dividend: &[Wire],
26 divisor: &[Wire],
27 ) -> (Vec<Wire>, Vec<Wire>) {
28 let inputs: Vec<Wire> = dividend.iter().chain(divisor).copied().collect();
29 let mut out = builder.call_hint(Self::new(), &[dividend.len(), divisor.len()], &inputs);
30 let remainder = out.split_off(dividend.len());
31 (out, remainder)
32 }
33}
34
35impl Default for BigUintDivideHint {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl Hint for BigUintDivideHint {
42 const NAME: &'static str = "binius.biguint_divide";
43
44 fn shape(&self, dimensions: &[usize]) -> (usize, usize) {
45 let [dividend_limbs, divisor_limbs] = dimensions else {
46 panic!("BigUintDivide requires 2 dimensions");
47 };
48 (*dividend_limbs + *divisor_limbs, *dividend_limbs + *divisor_limbs)
49 }
50
51 fn execute(&self, dimensions: &[usize], inputs: &[Word], outputs: &mut [Word]) {
52 let [n_dividend, n_divisor] = dimensions else {
53 panic!("BigUintDivide requires 2 dimensions");
54 };
55 let n_quotient = *n_dividend;
56 let n_remainder = *n_divisor;
57
58 let dividend_limbs = &inputs[0..*n_dividend];
59 let divisor_limbs = &inputs[*n_dividend..];
60
61 let dividend = num_biguint_from_u64_limbs(dividend_limbs.iter().map(|w| w.as_u64()));
62 let divisor = num_biguint_from_u64_limbs(divisor_limbs.iter().map(|w| w.as_u64()));
63
64 let zero = num_bigint::BigUint::ZERO;
65 let (quotient, remainder) = if divisor != zero {
66 (dividend.clone() / divisor.clone(), dividend % divisor)
67 } else {
68 (zero.clone(), zero)
69 };
70
71 for (i, limb) in quotient.iter_u64_digits().enumerate() {
73 if i < n_quotient {
74 outputs[i] = Word::from_u64(limb);
75 }
76 }
77 for i in quotient.iter_u64_digits().len()..n_quotient {
79 outputs[i] = Word::ZERO;
80 }
81
82 for (i, limb) in remainder.iter_u64_digits().enumerate() {
84 if i < n_remainder {
85 outputs[n_quotient + i] = Word::from_u64(limb);
86 }
87 }
88 for i in remainder.iter_u64_digits().len()..n_remainder {
90 outputs[n_quotient + i] = Word::ZERO;
91 }
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn test_biguint_divide_hint() {
101 let builder = CircuitBuilder::new();
102
103 let d0 = builder.add_constant_64(u64::MAX);
105 let d1 = builder.add_constant_64(u64::MAX);
106
107 let m = builder.add_constant_64(u64::MAX - 4);
108
109 let (q, r) = BigUintDivideHint::call(&builder, &[d0, d1], &[m]);
110
111 for &wire in q.iter().chain(&r) {
114 builder.mark_inout(wire);
115 }
116
117 let circuit = builder.build();
118 let mut w = circuit.new_witness_filler();
119 circuit.populate_wire_witness(&mut w).unwrap();
120
121 assert_eq!(r.len(), 1);
122 assert_eq!(w[r[0]], Word(24));
123
124 assert_eq!(q.len(), 2);
125 assert_eq!(w[q[0]], Word(5));
126 assert_eq!(w[q[1]], Word(1));
127 }
128
129 #[test]
130 fn test_biguint_divide_hint_div_by_zero() {
131 let builder = CircuitBuilder::new();
132
133 let d0 = builder.add_constant_64(u64::MAX);
134 let d1 = builder.add_constant_64(u64::MAX);
135
136 let m0 = builder.add_constant_64(0);
137 let m1 = builder.add_constant_64(0);
138
139 let (q, r) = BigUintDivideHint::call(&builder, &[d0, d1], &[m0, m1]);
140
141 for &wire in q.iter().chain(&r) {
144 builder.mark_inout(wire);
145 }
146
147 let circuit = builder.build();
148 let mut w = circuit.new_witness_filler();
149 circuit.populate_wire_witness(&mut w).unwrap();
150
151 assert_eq!(r.len(), 2);
152 assert_eq!(w[r[0]], Word(0));
153 assert_eq!(w[r[1]], Word(0));
154
155 assert_eq!(q.len(), 2);
156 assert_eq!(w[q[0]], Word(0));
157 assert_eq!(w[q[1]], Word(0));
158 }
159}