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