binius_circuits/bignum/
biguint.rs1use std::iter;
3
4use binius_core::word::Word;
5use binius_frontend::{CircuitBuilder, Wire, WitnessFiller};
6
7#[derive(Clone)]
13pub struct BigUint {
14 pub limbs: Vec<Wire>,
15}
16
17impl BigUint {
18 pub fn new_inout(b: &CircuitBuilder, num_limbs: usize) -> Self {
20 let limbs = (0..num_limbs).map(|_| b.add_inout()).collect();
21 BigUint { limbs }
22 }
23
24 pub fn new_witness(b: &CircuitBuilder, num_limbs: usize) -> Self {
26 let limbs = (0..num_limbs).map(|_| b.add_witness()).collect();
27 BigUint { limbs }
28 }
29
30 pub fn new_constant(b: &CircuitBuilder, num_biguint: &num_bigint::BigUint) -> Self {
32 let limbs = num_biguint
33 .iter_u64_digits()
34 .map(|limb| b.add_constant_64(limb))
35 .collect();
36 BigUint { limbs }
37 }
38
39 pub fn zero_unless(&self, b: &CircuitBuilder, cond: Wire) -> Self {
41 let zero = b.add_constant(Word::ZERO);
42 let limbs = self
43 .limbs
44 .iter()
45 .map(|&limb| b.select(cond, limb, zero))
46 .collect();
47 Self { limbs }
48 }
49
50 pub fn is_zero(&self, b: &CircuitBuilder) -> Wire {
52 let zero = b.add_constant(Word::ZERO);
53 let any_bit = self
54 .limbs
55 .iter()
56 .copied()
57 .reduce(|lhs, rhs| b.bor(lhs, rhs))
58 .unwrap_or(zero);
59 b.icmp_eq(any_bit, zero)
60 }
61
62 pub fn zero_extend(&self, b: &CircuitBuilder, new_limbs_len: usize) -> Self {
66 let zero = b.add_constant(Word::ZERO);
67 self.pad_limbs_to(new_limbs_len, zero)
68 }
69
70 pub fn pad_limbs_to(&self, new_limbs_len: usize, padding_value: Wire) -> Self {
74 let mut padded_limbs = self.limbs.clone();
75 if new_limbs_len > padded_limbs.len() {
76 padded_limbs.resize(new_limbs_len, padding_value);
77 }
78 Self {
79 limbs: padded_limbs,
80 }
81 }
82
83 pub fn split_at_limbs(mut self, at_limbs: usize) -> (Self, Self) {
86 let hi_limbs = self.limbs.split_off(at_limbs);
87 (self, Self { limbs: hi_limbs })
88 }
89
90 pub fn concat_limbs(&self, hi: &Self) -> Self {
93 let mut limbs = self.limbs.clone();
94 limbs.extend(&hi.limbs);
95 Self { limbs }
96 }
97
98 pub fn populate_limbs(&self, w: &mut WitnessFiller<'_>, limb_values: &[u64]) {
102 assert!(limb_values.len() == self.limbs.len());
103 for (&wire, &v) in iter::zip(&self.limbs, limb_values) {
104 w[wire] = Word::from_u64(v);
105 }
106 }
107}
108
109pub fn assert_eq(builder: &CircuitBuilder, name: impl Into<String>, a: &BigUint, b: &BigUint) {
119 assert_eq!(
120 a.limbs.len(),
121 b.limbs.len(),
122 "biguint assert_eq: inputs must have the same number of limbs"
123 );
124 let base_name = name.into();
125 for (i, (&a_l, &b_l)) in iter::zip(&a.limbs, &b.limbs).enumerate() {
126 builder.assert_eq(format!("{base_name}[{i}]"), a_l, b_l);
127 }
128}
129
130pub fn assert_eq_cond(
141 builder: &CircuitBuilder,
142 name: impl Into<String>,
143 a: &BigUint,
144 b: &BigUint,
145 cond: Wire,
146) {
147 assert_eq!(
148 a.limbs.len(),
149 b.limbs.len(),
150 "biguint assert_eq_cond: inputs must have the same number of limbs"
151 );
152 let base_name = name.into();
153 for (i, (&a_l, &b_l)) in iter::zip(&a.limbs, &b.limbs).enumerate() {
154 builder.assert_eq_cond(format!("{base_name}[{i}]"), a_l, b_l, cond);
155 }
156}
157
158pub fn select(builder: &CircuitBuilder, cond: Wire, t: &BigUint, f: &BigUint) -> BigUint {
172 assert_eq!(
173 t.limbs.len(),
174 f.limbs.len(),
175 "biguint select: inputs must have the same number of limbs"
176 );
177
178 let limbs = iter::zip(&t.limbs, &f.limbs)
179 .map(|(&l1, &l2)| builder.select(cond, l1, l2))
180 .collect();
181 BigUint { limbs }
182}
183
184pub fn num_biguint_from_u64_limbs<I>(limbs: I) -> num_bigint::BigUint
189where
190 I: IntoIterator,
191 I::Item: std::borrow::Borrow<u64>,
192{
193 use std::borrow::Borrow;
194
195 let bytes: Vec<u8> = limbs
196 .into_iter()
197 .flat_map(|limb| limb.borrow().to_le_bytes())
198 .collect();
199 num_bigint::BigUint::from_bytes_le(&bytes)
200}