snarkvm_fields/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
// Copyright 2024 Aleo Network Foundation
// This file is part of the snarkVM library.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![allow(clippy::module_inception)]
#![forbid(unsafe_code)]
#[macro_use]
extern crate thiserror;
#[macro_use]
mod macros;
pub mod errors;
pub use errors::*;
mod fp_256;
pub use fp_256::*;
mod fp_384;
pub use fp_384::*;
mod fp2;
pub use fp2::*;
pub mod fp6_3over2;
mod fp12_2over3over2;
pub use fp12_2over3over2::*;
mod legendre;
pub use legendre::*;
mod to_field_vec;
#[allow(unused_imports)]
pub use to_field_vec::*;
pub mod traits;
pub use traits::*;
use snarkvm_utilities::{
FromBytes,
ToBytes,
biginteger::*,
serialize::{CanonicalDeserialize, CanonicalDeserializeWithFlags, CanonicalSerialize, CanonicalSerializeWithFlags},
};
impl_field_to_biginteger!(Fp256, BigInteger256, Fp256Parameters);
impl_field_to_biginteger!(Fp384, BigInteger384, Fp384Parameters);
impl_primefield_serializer!(Fp256, Fp256Parameters, 32);
impl_primefield_serializer!(Fp384, Fp384Parameters, 48);
// Given a vector of field elements {v_i}, compute the vector {v_i^(-1)}
pub fn batch_inversion<F: Field>(v: &mut [F]) {
batch_inversion_and_mul(v, &F::one());
}
#[cfg(feature = "serial")]
// Given a vector of field elements {v_i}, compute the vector {coeff * v_i^(-1)}
pub fn batch_inversion_and_mul<F: Field>(v: &mut [F], coeff: &F) {
serial_batch_inversion_and_mul(v, coeff);
}
#[cfg(not(feature = "serial"))]
// Given a vector of field elements {v_i}, compute the vector {coeff * v_i^(-1)}
pub fn batch_inversion_and_mul<F: Field>(v: &mut [F], coeff: &F) {
use rayon::prelude::*;
// Divide the vector v evenly between all available cores
let min_elements_per_thread = 1;
let num_cpus_available = snarkvm_utilities::parallel::max_available_threads();
let num_elems = v.len();
let num_elem_per_thread = min_elements_per_thread.max(num_elems / num_cpus_available);
// Batch invert in parallel, without copying the vector
v.par_chunks_mut(num_elem_per_thread).for_each(|chunk| {
serial_batch_inversion_and_mul(chunk, coeff);
});
}
/// Given a vector of field elements {v_i}, compute the vector {coeff * v_i^(-1)}.
/// This method is explicitly single-threaded.
fn serial_batch_inversion_and_mul<F: Field>(v: &mut [F], coeff: &F) {
// Montgomery’s Trick and Fast Implementation of Masked AES
// Genelle, Prouff and Quisquater
// Section 3.2
// but with an optimization to multiply every element in the returned vector by
// coeff
// First pass: compute [a, ab, abc, ...]
let mut prod = Vec::with_capacity(v.len());
let mut tmp = F::one();
for f in v.iter().filter(|f| !f.is_zero()) {
tmp.mul_assign(f);
prod.push(tmp);
}
// Invert `tmp`.
tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero.
// Multiply product by coeff, so all inverses will be scaled by coeff
tmp *= coeff;
// Second pass: iterate backwards to compute inverses
for (f, s) in v.iter_mut()
// Backwards
.rev()
// Ignore normalized elements
.filter(|f| !f.is_zero())
// Backwards, skip last element, fill in one for last term.
.zip(prod.into_iter().rev().skip(1).chain(Some(F::one())))
{
// tmp := tmp * f; f := tmp * s = 1/f
let new_tmp = tmp * *f;
*f = tmp * s;
tmp = new_tmp;
}
}