Skip to main content

p3_poseidon1/
generic.rs

1//! Generic implementations of Poseidon1 linear layers.
2//!
3//! This module provides two things:
4//!
5//! 1. [`GenericPoseidon1LinearLayers`] — a trait with default dense/sparse linear
6//!    layer methods, used by field-specific implementations that only need to
7//!    override individual operations.
8//!
9//! 2. [`Poseidon1ExternalLayerGeneric`] and [`Poseidon1InternalLayerGeneric`] —
10//!    ready-to-use layer structs that implement [`FullRoundLayer`] and
11//!    [`PartialRoundLayer`] for **any** field with a conforming MDS permutation.
12//!    These are suitable for fields without hand-tuned SIMD implementations
13//!    (e.g., Goldilocks, Mersenne-31).
14
15use core::marker::PhantomData;
16
17use p3_field::{Algebra, Field, InjectiveMonomial};
18use p3_symmetric::Permutation;
19
20use crate::external::{
21    FullRoundConstants, FullRoundLayer, FullRoundLayerConstructor,
22    full_round_initial_permute_state, full_round_terminal_permute_state, mds_multiply,
23};
24use crate::internal::{
25    PartialRoundConstants, PartialRoundLayer, PartialRoundLayerConstructor, cheap_matmul,
26    partial_permute_state, textbook_partial_permute_state,
27};
28
29/// Generic round-constant addition followed by S-box evaluation: `val <- (val + rc)^D`.
30#[inline(always)]
31pub fn add_rc_and_sbox_generic<F: Field, A: Algebra<F> + InjectiveMonomial<D>, const D: u64>(
32    val: &mut A,
33    rc: F,
34) {
35    *val += rc;
36    *val = val.injective_exp_n();
37}
38
39/// Generic linear layer trait for Poseidon1.
40///
41/// Provides default dense MDS multiplication and sparse matrix multiplication
42/// for use when no field-specific optimized implementation is available.
43pub trait GenericPoseidon1LinearLayers<F: Field, const WIDTH: usize>: Sync {
44    /// Dense MDS matrix-vector multiplication in O(t^2).
45    ///
46    /// Used for the dense transition matrix in partial rounds.
47    fn mds_multiply<R: Algebra<F>>(state: &mut [R; WIDTH], mds: &[[F; WIDTH]; WIDTH]) {
48        mds_multiply(state, mds);
49    }
50
51    /// MDS multiplication dispatched via a permutation trait.
52    ///
53    /// Used in full rounds. Concrete fields can provide sub-O(t^2)
54    /// implementations (e.g., Karatsuba convolution for circulant matrices).
55    fn mds_permute<R: Algebra<F>>(state: &mut [R; WIDTH], mds: &impl Permutation<[R; WIDTH]>) {
56        mds.permute_mut(state);
57    }
58
59    /// Sparse matrix-vector multiplication for partial rounds in O(t).
60    fn cheap_matmul<R: Algebra<F>>(state: &mut [R; WIDTH], first_row: &[F; WIDTH], v: &[F; WIDTH]) {
61        cheap_matmul(state, first_row, v);
62    }
63}
64
65/// Generic external (full round) layer for the Poseidon1 permutation.
66///
67/// Dispatches MDS multiplication through a [`Permutation`] trait instance,
68/// allowing any field with a conforming MDS implementation to use the
69/// optimized Poseidon1 without a field-specific layer.
70///
71/// The `Mds` type must implement [`Default`] (typically a zero-sized struct)
72/// and [`Permutation<[A; WIDTH]>`] for each ring type `A` used.
73#[derive(Debug, Clone)]
74pub struct Poseidon1ExternalLayerGeneric<F, Mds, const WIDTH: usize> {
75    constants: FullRoundConstants<F, WIDTH>,
76    _mds: PhantomData<Mds>,
77}
78
79impl<F: Field, Mds, const WIDTH: usize> FullRoundLayerConstructor<F, WIDTH>
80    for Poseidon1ExternalLayerGeneric<F, Mds, WIDTH>
81{
82    fn new_from_constants(constants: FullRoundConstants<F, WIDTH>) -> Self {
83        Self {
84            constants,
85            _mds: PhantomData,
86        }
87    }
88}
89
90impl<F, A, Mds, const WIDTH: usize, const D: u64> FullRoundLayer<A, WIDTH, D>
91    for Poseidon1ExternalLayerGeneric<F, Mds, WIDTH>
92where
93    F: Field + InjectiveMonomial<D>,
94    A: Algebra<F> + InjectiveMonomial<D>,
95    Mds: Permutation<[A; WIDTH]> + Default + Sync + Clone,
96{
97    fn permute_state_initial(&self, state: &mut [A; WIDTH]) {
98        let mds = Mds::default();
99        full_round_initial_permute_state::<F, A, _, WIDTH, D>(state, &self.constants, &mds);
100    }
101
102    fn permute_state_terminal(&self, state: &mut [A; WIDTH]) {
103        let mds = Mds::default();
104        full_round_terminal_permute_state::<F, A, _, WIDTH, D>(state, &self.constants, &mds);
105    }
106}
107
108/// Generic internal (partial round) layer for the Poseidon1 permutation.
109///
110/// Uses the sparse matrix decomposition for O(t) partial rounds.
111///
112/// Works with any field that supports the required algebra operations.
113#[derive(Debug, Clone)]
114pub struct Poseidon1InternalLayerGeneric<F, const WIDTH: usize> {
115    constants: PartialRoundConstants<F, WIDTH>,
116}
117
118impl<F: Field, const WIDTH: usize> PartialRoundLayerConstructor<F, WIDTH>
119    for Poseidon1InternalLayerGeneric<F, WIDTH>
120{
121    fn new_from_constants(constants: PartialRoundConstants<F, WIDTH>) -> Self {
122        Self { constants }
123    }
124}
125
126impl<F, A, const WIDTH: usize, const D: u64> PartialRoundLayer<A, WIDTH, D>
127    for Poseidon1InternalLayerGeneric<F, WIDTH>
128where
129    F: Field + InjectiveMonomial<D>,
130    A: Algebra<F> + InjectiveMonomial<D>,
131{
132    fn permute_state(&self, state: &mut [A; WIDTH]) {
133        partial_permute_state::<F, A, WIDTH, D>(state, &self.constants);
134    }
135}
136
137/// Textbook internal (partial round) layer for the Poseidon1 permutation.
138///
139/// Uses the MDS permutation per round with forward-substituted scalar constants.
140/// Each partial round adds a single scalar to `state[0]`, applies the S-box, and
141/// multiplies by the MDS. After all rounds, a residual vector is added.
142///
143/// Use this instead of [`Poseidon1InternalLayerGeneric`] when the MDS permutation
144/// is very fast (e.g., Karatsuba convolution for power-of-2 circulant matrices).
145#[derive(Debug, Clone)]
146pub struct Poseidon1InternalLayerTextbook<F, Mds, const WIDTH: usize> {
147    constants: PartialRoundConstants<F, WIDTH>,
148    _mds: PhantomData<Mds>,
149}
150
151impl<F: Field, Mds, const WIDTH: usize> PartialRoundLayerConstructor<F, WIDTH>
152    for Poseidon1InternalLayerTextbook<F, Mds, WIDTH>
153{
154    fn new_from_constants(constants: PartialRoundConstants<F, WIDTH>) -> Self {
155        Self {
156            constants,
157            _mds: PhantomData,
158        }
159    }
160}
161
162impl<F, A, Mds, const WIDTH: usize, const D: u64> PartialRoundLayer<A, WIDTH, D>
163    for Poseidon1InternalLayerTextbook<F, Mds, WIDTH>
164where
165    F: Field + InjectiveMonomial<D>,
166    A: Algebra<F> + InjectiveMonomial<D>,
167    Mds: Permutation<[A; WIDTH]> + Default + Sync + Clone,
168{
169    fn permute_state(&self, state: &mut [A; WIDTH]) {
170        let mds = Mds::default();
171        textbook_partial_permute_state::<F, A, _, WIDTH, D>(state, &self.constants, &mds);
172    }
173}