use std::cmp::max;
use midnight_proofs::circuit::Value;
use num_bigint::BigUint;
use num_traits::{One, Zero};
#[cfg(any(test, feature = "testing"))]
use crate::testing_utils::Sampleable;
use crate::{
field::foreign::util::{big_from_limbs, big_to_limbs},
types::{AssignedNative, InnerConstants, InnerValue},
utils::util::big_to_fe,
CircuitField,
};
pub(crate) const LOG2_BASE: u32 = 96;
#[derive(Clone, Debug)]
#[must_use]
pub struct AssignedBigUint<F: CircuitField> {
pub(crate) limbs: Vec<AssignedNative<F>>,
pub(crate) limb_size_bounds: Vec<u32>,
}
impl<F: CircuitField> InnerValue for AssignedBigUint<F> {
type Element = BigUint;
fn value(&self) -> Value<BigUint> {
let base = BigUint::one() << LOG2_BASE;
let limbs_as_big = self.limbs.iter().map(|l| l.value().copied().map(|v| v.to_biguint()));
let value: Value<Vec<BigUint>> = Value::from_iter(limbs_as_big);
value.map(|limbs| big_from_limbs(&base, &limbs))
}
}
impl<F: CircuitField> InnerConstants for AssignedBigUint<F> {
fn inner_zero() -> BigUint {
BigUint::zero()
}
fn inner_one() -> Self::Element {
BigUint::one()
}
}
impl<F: CircuitField> AssignedBigUint<F> {
pub fn as_public_input(element: &BigUint, nb_bits: u32) -> Vec<F> {
biguint_to_limbs(element, Some(nb_bits.div_ceil(LOG2_BASE)))
}
}
impl<F: CircuitField> PartialEq for AssignedBigUint<F> {
fn eq(&self, other: &Self) -> bool {
self.limbs.iter().zip(other.limbs.iter()).all(|(s, o)| s == o)
}
}
#[cfg(any(test, feature = "testing"))]
pub(crate) const TEST_NB_BITS: u32 = 1024;
#[cfg(any(test, feature = "testing"))]
impl<F: CircuitField> Sampleable for AssignedBigUint<F> {
fn sample_inner(mut rng: impl rand::RngCore) -> BigUint {
num_bigint::RandBigInt::gen_biguint(&mut rng, TEST_NB_BITS as u64)
}
}
impl<F: CircuitField> AssignedBigUint<F> {
pub fn nb_bits(&self) -> u32 {
self.limb_size_bounds
.iter()
.rev()
.fold(BigUint::zero(), |acc, bound| {
(acc << LOG2_BASE) + (BigUint::one() << bound) - BigUint::one()
})
.bits() as u32
}
pub fn is_normalized(&self) -> bool {
self.limb_size_bounds.iter().all(|bound| *bound <= LOG2_BASE)
}
}
pub(crate) fn bound_of_addition(bound1: u32, bound2: u32) -> u32 {
if bound1 == 0 {
return bound2;
}
if bound2 == 0 {
return bound1;
}
1 + max(bound1, bound2)
}
pub(crate) fn biguint_to_limbs<F: CircuitField>(value: &BigUint, nb_limbs: Option<u32>) -> Vec<F> {
let nb_limbs = nb_limbs.unwrap_or(value.bits().div_ceil(LOG2_BASE as u64) as u32);
big_to_limbs(nb_limbs, &(BigUint::from(1u8) << LOG2_BASE), value)
.into_iter()
.map(big_to_fe::<F>)
.collect()
}