From c1ec4b7830d9b1bce8a0d3f3b7c49c79de6ae136 Mon Sep 17 00:00:00 2001 From: Alex Xiong Date: Wed, 8 Apr 2026 14:47:20 +0800 Subject: [PATCH] Introduce DeferredField trait with eager implementations Add the `deferred` module with a `DeferredField` trait that enables accumulating multiple unreduced products before performing a single reduction. The trait uses an accumulation-based API: callers feed factor pairs into an `Accumulator` via `mul_accumulate` / `square_accumulate`, then call `reduce` once at the end. For now, both Fp and Fq implement the trait with `Accumulator = Self`, performing eager reduction on each accumulation. A later commit will swap in a wide accumulator that defers reduction for real. The module is feature-gated behind the `deferred` feature flag. --- Cargo.toml | 1 + src/deferred.rs | 29 +++++++++++++++++++++++++++++ src/fields/fp.rs | 22 ++++++++++++++++++++++ src/fields/fq.rs | 22 ++++++++++++++++++++++ src/lib.rs | 3 +++ 5 files changed, 77 insertions(+) create mode 100644 src/deferred.rs diff --git a/Cargo.toml b/Cargo.toml index 1b869fb..a11765c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -71,4 +71,5 @@ gpu = ["alloc", "ec-gpu"] sqrt-table = ["alloc", "lazy_static"] repr-c = [] uninline-portable = [] +deferred = [] serde = ["hex", "serde_crate"] diff --git a/src/deferred.rs b/src/deferred.rs new file mode 100644 index 0000000..1500ece --- /dev/null +++ b/src/deferred.rs @@ -0,0 +1,29 @@ +//! Deferred normalization for field arithmetic. +//! +//! This module provides the [`DeferredField`] trait, which enables accumulating +//! multiple unreduced products before performing a single expensive reduction. +//! This is useful for operations like inner products where many multiplications +//! feed into a sum. + +use core::fmt::Debug; + +/// A trait for fields that support deferred reduction of products. +/// +/// Instead of reducing each multiplication result immediately, callers +/// accumulate products into an [`Accumulator`](Self::Accumulator) via +/// [`mul_accumulate`](Self::mul_accumulate) and +/// [`square_accumulate`](Self::square_accumulate), then perform a single +/// reduction at the end with [`reduce`](Self::reduce). +pub trait DeferredField: ff::Field { + /// A wide accumulator for unreduced products. + type Accumulator: Copy + Clone + Debug + Default; + + /// Multiplies `a` by `b` and adds the result into `acc`. + fn mul_accumulate(acc: &mut Self::Accumulator, a: &Self, b: &Self); + + /// Squares `a` and adds the result into `acc`. + fn square_accumulate(acc: &mut Self::Accumulator, a: &Self); + + /// Reduces the accumulator to a canonical field element. + fn reduce(acc: Self::Accumulator) -> Self; +} diff --git a/src/fields/fp.rs b/src/fields/fp.rs index 5e17c99..a613a5e 100644 --- a/src/fields/fp.rs +++ b/src/fields/fp.rs @@ -12,6 +12,8 @@ use lazy_static::lazy_static; use ff::{FieldBits, PrimeFieldBits}; use crate::arithmetic::{adc, mac, sbb, SqrtTableHelpers}; +#[cfg(feature = "deferred")] +use crate::deferred::DeferredField; #[cfg(feature = "sqrt-table")] use crate::arithmetic::SqrtTables; @@ -477,6 +479,26 @@ impl Fp { } } +#[cfg(feature = "deferred")] +impl DeferredField for Fp { + type Accumulator = Fp; + + #[inline] + fn mul_accumulate(acc: &mut Fp, a: &Fp, b: &Fp) { + *acc += *a * *b; + } + + #[inline] + fn square_accumulate(acc: &mut Fp, a: &Fp) { + *acc += a.square(); + } + + #[inline] + fn reduce(acc: Fp) -> Fp { + acc + } +} + impl From for [u8; 32] { fn from(value: Fp) -> [u8; 32] { value.to_repr() diff --git a/src/fields/fq.rs b/src/fields/fq.rs index acb6914..8b0e46f 100644 --- a/src/fields/fq.rs +++ b/src/fields/fq.rs @@ -12,6 +12,8 @@ use lazy_static::lazy_static; use ff::{FieldBits, PrimeFieldBits}; use crate::arithmetic::{adc, mac, sbb, SqrtTableHelpers}; +#[cfg(feature = "deferred")] +use crate::deferred::DeferredField; #[cfg(feature = "sqrt-table")] use crate::arithmetic::SqrtTables; @@ -477,6 +479,26 @@ impl Fq { } } +#[cfg(feature = "deferred")] +impl DeferredField for Fq { + type Accumulator = Fq; + + #[inline] + fn mul_accumulate(acc: &mut Fq, a: &Fq, b: &Fq) { + *acc += *a * *b; + } + + #[inline] + fn square_accumulate(acc: &mut Fq, a: &Fq) { + *acc += a.square(); + } + + #[inline] + fn reduce(acc: Fq) -> Fq { + acc + } +} + impl From for [u8; 32] { fn from(value: Fq) -> [u8; 32] { value.to_repr() diff --git a/src/lib.rs b/src/lib.rs index 188134b..8165b9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,6 +22,9 @@ mod curves; mod fields; pub mod arithmetic; +#[cfg(feature = "deferred")] +#[cfg_attr(docsrs, doc(cfg(feature = "deferred")))] +pub mod deferred; pub mod pallas; pub mod vesta;