Skip to main content

snarkvm_utilities/biginteger/
bigint_256.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use std::{
17    cmp,
18    io::{Read, Result as IoResult, Write},
19};
20
21use crate::{
22    FromBits,
23    FromBytes,
24    ToBits,
25    ToBytes,
26    biginteger::BigInteger,
27    bititerator::{BitIteratorBE, BitIteratorLE},
28};
29
30use anyhow::Result;
31use core::fmt::{Debug, Display};
32use num_bigint::BigUint;
33use rand::{
34    Rng,
35    distributions::{Distribution, Standard},
36};
37use zeroize::Zeroize;
38
39#[derive(Copy, Clone, PartialEq, Eq, Default, Hash, Zeroize)]
40pub struct BigInteger256(pub [u64; 4]);
41
42impl BigInteger256 {
43    pub const fn new(value: [u64; 4]) -> Self {
44        BigInteger256(value)
45    }
46}
47
48impl crate::biginteger::BigInteger for BigInteger256 {
49    const NUM_LIMBS: usize = 4;
50
51    #[inline]
52    fn add_nocarry(&mut self, other: &Self) -> bool {
53        #[cfg(target_arch = "x86_64")]
54        unsafe {
55            use core::arch::x86_64::_addcarry_u64;
56            let mut carry = 0;
57            carry = _addcarry_u64(carry, self.0[0], other.0[0], &mut self.0[0]);
58            carry = _addcarry_u64(carry, self.0[1], other.0[1], &mut self.0[1]);
59            carry = _addcarry_u64(carry, self.0[2], other.0[2], &mut self.0[2]);
60            carry = _addcarry_u64(carry, self.0[3], other.0[3], &mut self.0[3]);
61            carry != 0
62        }
63        #[cfg(not(target_arch = "x86_64"))]
64        {
65            let mut carry = 0;
66            carry = super::arithmetic::adc(&mut self.0[0], other.0[0], carry);
67            carry = super::arithmetic::adc(&mut self.0[1], other.0[1], carry);
68            carry = super::arithmetic::adc(&mut self.0[2], other.0[2], carry);
69            carry = super::arithmetic::adc(&mut self.0[3], other.0[3], carry);
70            carry != 0
71        }
72    }
73
74    #[inline]
75    fn sub_noborrow(&mut self, other: &Self) -> bool {
76        #[cfg(target_arch = "x86_64")]
77        unsafe {
78            use core::arch::x86_64::_subborrow_u64;
79            let mut borrow = 0;
80            borrow = _subborrow_u64(borrow, self.0[0], other.0[0], &mut self.0[0]);
81            borrow = _subborrow_u64(borrow, self.0[1], other.0[1], &mut self.0[1]);
82            borrow = _subborrow_u64(borrow, self.0[2], other.0[2], &mut self.0[2]);
83            borrow = _subborrow_u64(borrow, self.0[3], other.0[3], &mut self.0[3]);
84            borrow != 0
85        }
86        #[cfg(not(target_arch = "x86_64"))]
87        {
88            let mut borrow = 0;
89            borrow = super::arithmetic::sbb(&mut self.0[0], other.0[0], borrow);
90            borrow = super::arithmetic::sbb(&mut self.0[1], other.0[1], borrow);
91            borrow = super::arithmetic::sbb(&mut self.0[2], other.0[2], borrow);
92            borrow = super::arithmetic::sbb(&mut self.0[3], other.0[3], borrow);
93            borrow != 0
94        }
95    }
96
97    #[inline]
98    fn mul2(&mut self) {
99        let mut last = 0;
100        for i in &mut self.0 {
101            let tmp = *i >> 63;
102            *i <<= 1;
103            *i |= last;
104            last = tmp;
105        }
106    }
107
108    #[inline]
109    fn muln(&mut self, mut n: u32) {
110        if n >= 64 * 4 {
111            *self = Self::from(0);
112            return;
113        }
114        while n >= 64 {
115            let mut t = 0;
116            for i in &mut self.0 {
117                std::mem::swap(&mut t, i);
118            }
119            n -= 64;
120        }
121        if n > 0 {
122            let mut t = 0;
123            for i in &mut self.0 {
124                let t2 = *i >> (64 - n);
125                *i <<= n;
126                *i |= t;
127                t = t2;
128            }
129        }
130    }
131
132    #[inline]
133    fn div2(&mut self) {
134        let mut t = 0;
135        for i in self.0.iter_mut().rev() {
136            let t2 = *i << 63;
137            *i >>= 1;
138            *i |= t;
139            t = t2;
140        }
141    }
142
143    #[inline]
144    fn divn(&mut self, mut n: u32) {
145        if n >= 64 * 4 {
146            *self = Self::from(0);
147            return;
148        }
149        while n >= 64 {
150            let mut t = 0;
151            for i in self.0.iter_mut().rev() {
152                std::mem::swap(&mut t, i);
153            }
154            n -= 64;
155        }
156        if n > 0 {
157            let mut t = 0;
158            for i in self.0.iter_mut().rev() {
159                let t2 = *i << (64 - n);
160                *i >>= n;
161                *i |= t;
162                t = t2;
163            }
164        }
165    }
166
167    #[inline]
168    fn is_odd(&self) -> bool {
169        self.0[0] & 1 == 1
170    }
171
172    #[inline]
173    fn is_even(&self) -> bool {
174        !self.is_odd()
175    }
176
177    #[inline]
178    fn is_zero(&self) -> bool {
179        self.0.iter().all(|&e| e == 0)
180    }
181
182    #[inline]
183    fn num_bits(&self) -> u32 {
184        let mut ret = 4 * 64;
185        for i in self.0.iter().rev() {
186            let leading = i.leading_zeros();
187            ret -= leading;
188            if leading != 64 {
189                break;
190            }
191        }
192        ret
193    }
194
195    #[inline]
196    fn get_bit(&self, i: usize) -> bool {
197        if i >= 64 * 4 {
198            false
199        } else {
200            let limb = i / 64;
201            let bit = i - (64 * limb);
202            (self.0[limb] & (1 << bit)) != 0
203        }
204    }
205
206    #[inline]
207    fn to_biguint(&self) -> num_bigint::BigUint {
208        BigUint::from_bytes_le(&self.to_bytes_le().unwrap())
209    }
210
211    #[inline]
212    fn find_wnaf(&self) -> Vec<i64> {
213        let mut res = Vec::new();
214        let mut e = *self;
215        while !e.is_zero() {
216            let z: i64;
217            if e.is_odd() {
218                z = 2 - (e.0[0] % 4) as i64;
219                if z >= 0 {
220                    e.sub_noborrow(&Self::from(z as u64));
221                } else {
222                    e.add_nocarry(&Self::from((-z) as u64));
223                }
224            } else {
225                z = 0;
226            }
227            res.push(z);
228            e.div2();
229        }
230        res
231    }
232}
233
234impl ToBits for BigInteger256 {
235    #[doc = " Returns `self` as a boolean array in little-endian order, with trailing zeros."]
236    fn write_bits_le(&self, vec: &mut Vec<bool>) {
237        let iter = BitIteratorLE::new(self);
238        vec.reserve(iter.len());
239        vec.extend(iter);
240    }
241
242    #[doc = " Returns `self` as a boolean array in big-endian order, with leading zeros."]
243    fn write_bits_be(&self, vec: &mut Vec<bool>) {
244        let iter = BitIteratorBE::new(self);
245        vec.reserve(iter.len());
246        vec.extend(iter);
247    }
248}
249
250impl FromBits for BigInteger256 {
251    #[doc = " Returns a `BigInteger` by parsing a slice of bits in little-endian format"]
252    #[doc = " and transforms it into a slice of little-endian u64 elements."]
253    fn from_bits_le(bits: &[bool]) -> Result<Self> {
254        let mut res = Self::default();
255        for (i, bits64) in bits.chunks(64).enumerate() {
256            let mut acc: u64 = 0;
257            for bit in bits64.iter().rev() {
258                acc <<= 1;
259                acc += *bit as u64;
260            }
261            res.0[i] = acc;
262        }
263        Ok(res)
264    }
265
266    #[doc = " Returns a `BigInteger` by parsing a slice of bits in big-endian format"]
267    #[doc = " and transforms it into a slice of little-endian u64 elements."]
268    fn from_bits_be(bits: &[bool]) -> Result<Self> {
269        let mut res = Self::default();
270        for (i, bits64) in bits.rchunks(64).enumerate() {
271            let mut acc: u64 = 0;
272            for bit in bits64.iter() {
273                acc <<= 1;
274                acc += *bit as u64;
275            }
276            res.0[i] = acc;
277        }
278        Ok(res)
279    }
280}
281
282impl ToBytes for BigInteger256 {
283    #[inline]
284    fn write_le<W: Write>(&self, writer: W) -> IoResult<()> {
285        let mut arr = [0u8; 8 * 4];
286        for (i, num) in self.0.iter().enumerate() {
287            arr[i * 8..(i + 1) * 8].copy_from_slice(&num.to_le_bytes());
288        }
289        arr.write_le(writer)
290    }
291}
292
293impl FromBytes for BigInteger256 {
294    #[inline]
295    fn read_le<R: Read>(reader: R) -> IoResult<Self> {
296        <[u64; 4]>::read_le(reader).map(Self::new)
297    }
298}
299
300impl Debug for BigInteger256 {
301    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302        for i in self.0.iter().rev() {
303            write!(f, "{:016X}", *i)?;
304        }
305        Ok(())
306    }
307}
308
309impl Display for BigInteger256 {
310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311        write!(f, "{}", self.to_biguint())
312    }
313}
314
315impl Ord for BigInteger256 {
316    #[inline]
317    fn cmp(&self, other: &Self) -> cmp::Ordering {
318        for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) {
319            match a.cmp(b) {
320                cmp::Ordering::Less => return cmp::Ordering::Less,
321                cmp::Ordering::Greater => return cmp::Ordering::Greater,
322                _ => continue,
323            }
324        }
325        cmp::Ordering::Equal
326    }
327}
328
329impl PartialOrd for BigInteger256 {
330    #[inline]
331    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
332        Some(self.cmp(other))
333    }
334}
335
336impl Distribution<BigInteger256> for Standard {
337    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> BigInteger256 {
338        BigInteger256(rng.r#gen())
339    }
340}
341
342impl AsMut<[u64]> for BigInteger256 {
343    #[inline]
344    fn as_mut(&mut self) -> &mut [u64] {
345        &mut self.0
346    }
347}
348
349impl AsRef<[u64]> for BigInteger256 {
350    #[inline]
351    fn as_ref(&self) -> &[u64] {
352        &self.0
353    }
354}
355
356impl From<u64> for BigInteger256 {
357    #[inline]
358    fn from(val: u64) -> BigInteger256 {
359        let mut repr = Self::default();
360        repr.0[0] = val;
361        repr
362    }
363}