twenty_first/tip5/
mod.rs

1//! The arithmetization-oriented, cryptographic hash function Tip5.
2//!
3//! This module contains the reference implementation of [“The Tip5 Hash
4//! Function for Recursive STARKs”](https://eprint.iacr.org/2023/107.pdf).
5
6use std::hash::Hasher;
7
8use arbitrary::Arbitrary;
9use get_size2::GetSize;
10use itertools::Itertools;
11use num_traits::ConstOne;
12use num_traits::ConstZero;
13use serde::Deserialize;
14use serde::Serialize;
15
16use crate::math::x_field_element::EXTENSION_DEGREE;
17use crate::prelude::BFieldCodec;
18use crate::prelude::BFieldElement;
19use crate::prelude::XFieldElement;
20use crate::util_types::sponge::Domain;
21use crate::util_types::sponge::Sponge;
22pub use digest::Digest;
23
24pub const STATE_SIZE: usize = 16;
25pub const NUM_SPLIT_AND_LOOKUP: usize = 4;
26pub const LOG2_STATE_SIZE: usize = 4;
27pub const CAPACITY: usize = 6;
28pub const RATE: usize = 10;
29pub const NUM_ROUNDS: usize = 5;
30
31pub mod digest;
32
33/// The lookup table with a high algebraic degree used in the TIP-5 permutation. To verify its
34/// correctness, see the test “lookup_table_is_correct.”
35pub const LOOKUP_TABLE: [u8; 256] = [
36    0, 7, 26, 63, 124, 215, 85, 254, 214, 228, 45, 185, 140, 173, 33, 240, 29, 177, 176, 32, 8,
37    110, 87, 202, 204, 99, 150, 106, 230, 14, 235, 128, 213, 239, 212, 138, 23, 130, 208, 6, 44,
38    71, 93, 116, 146, 189, 251, 81, 199, 97, 38, 28, 73, 179, 95, 84, 152, 48, 35, 119, 49, 88,
39    242, 3, 148, 169, 72, 120, 62, 161, 166, 83, 175, 191, 137, 19, 100, 129, 112, 55, 221, 102,
40    218, 61, 151, 237, 68, 164, 17, 147, 46, 234, 203, 216, 22, 141, 65, 57, 123, 12, 244, 54, 219,
41    231, 96, 77, 180, 154, 5, 253, 133, 165, 98, 195, 205, 134, 245, 30, 9, 188, 59, 142, 186, 197,
42    181, 144, 92, 31, 224, 163, 111, 74, 58, 69, 113, 196, 67, 246, 225, 10, 121, 50, 60, 157, 90,
43    122, 2, 250, 101, 75, 178, 159, 24, 36, 201, 11, 243, 132, 198, 190, 114, 233, 39, 52, 21, 209,
44    108, 238, 91, 187, 18, 104, 194, 37, 153, 34, 200, 143, 126, 155, 236, 118, 64, 80, 172, 89,
45    94, 193, 135, 183, 86, 107, 252, 13, 167, 206, 136, 220, 207, 103, 171, 160, 76, 182, 227, 217,
46    158, 56, 174, 4, 66, 109, 139, 162, 184, 211, 249, 47, 125, 232, 117, 43, 16, 42, 127, 20, 241,
47    25, 149, 105, 156, 51, 53, 168, 145, 247, 223, 79, 78, 226, 15, 222, 82, 115, 70, 210, 27, 41,
48    1, 170, 40, 131, 192, 229, 248, 255,
49];
50
51/// The round constants used in the Tip5 permutation. To verify their correctness, see the test
52/// “round_constants_are_correct.”
53pub const ROUND_CONSTANTS: [BFieldElement; NUM_ROUNDS * STATE_SIZE] = [
54    BFieldElement::new(13630775303355457758),
55    BFieldElement::new(16896927574093233874),
56    BFieldElement::new(10379449653650130495),
57    BFieldElement::new(1965408364413093495),
58    BFieldElement::new(15232538947090185111),
59    BFieldElement::new(15892634398091747074),
60    BFieldElement::new(3989134140024871768),
61    BFieldElement::new(2851411912127730865),
62    BFieldElement::new(8709136439293758776),
63    BFieldElement::new(3694858669662939734),
64    BFieldElement::new(12692440244315327141),
65    BFieldElement::new(10722316166358076749),
66    BFieldElement::new(12745429320441639448),
67    BFieldElement::new(17932424223723990421),
68    BFieldElement::new(7558102534867937463),
69    BFieldElement::new(15551047435855531404),
70    BFieldElement::new(17532528648579384106),
71    BFieldElement::new(5216785850422679555),
72    BFieldElement::new(15418071332095031847),
73    BFieldElement::new(11921929762955146258),
74    BFieldElement::new(9738718993677019874),
75    BFieldElement::new(3464580399432997147),
76    BFieldElement::new(13408434769117164050),
77    BFieldElement::new(264428218649616431),
78    BFieldElement::new(4436247869008081381),
79    BFieldElement::new(4063129435850804221),
80    BFieldElement::new(2865073155741120117),
81    BFieldElement::new(5749834437609765994),
82    BFieldElement::new(6804196764189408435),
83    BFieldElement::new(17060469201292988508),
84    BFieldElement::new(9475383556737206708),
85    BFieldElement::new(12876344085611465020),
86    BFieldElement::new(13835756199368269249),
87    BFieldElement::new(1648753455944344172),
88    BFieldElement::new(9836124473569258483),
89    BFieldElement::new(12867641597107932229),
90    BFieldElement::new(11254152636692960595),
91    BFieldElement::new(16550832737139861108),
92    BFieldElement::new(11861573970480733262),
93    BFieldElement::new(1256660473588673495),
94    BFieldElement::new(13879506000676455136),
95    BFieldElement::new(10564103842682358721),
96    BFieldElement::new(16142842524796397521),
97    BFieldElement::new(3287098591948630584),
98    BFieldElement::new(685911471061284805),
99    BFieldElement::new(5285298776918878023),
100    BFieldElement::new(18310953571768047354),
101    BFieldElement::new(3142266350630002035),
102    BFieldElement::new(549990724933663297),
103    BFieldElement::new(4901984846118077401),
104    BFieldElement::new(11458643033696775769),
105    BFieldElement::new(8706785264119212710),
106    BFieldElement::new(12521758138015724072),
107    BFieldElement::new(11877914062416978196),
108    BFieldElement::new(11333318251134523752),
109    BFieldElement::new(3933899631278608623),
110    BFieldElement::new(16635128972021157924),
111    BFieldElement::new(10291337173108950450),
112    BFieldElement::new(4142107155024199350),
113    BFieldElement::new(16973934533787743537),
114    BFieldElement::new(11068111539125175221),
115    BFieldElement::new(17546769694830203606),
116    BFieldElement::new(5315217744825068993),
117    BFieldElement::new(4609594252909613081),
118    BFieldElement::new(3350107164315270407),
119    BFieldElement::new(17715942834299349177),
120    BFieldElement::new(9600609149219873996),
121    BFieldElement::new(12894357635820003949),
122    BFieldElement::new(4597649658040514631),
123    BFieldElement::new(7735563950920491847),
124    BFieldElement::new(1663379455870887181),
125    BFieldElement::new(13889298103638829706),
126    BFieldElement::new(7375530351220884434),
127    BFieldElement::new(3502022433285269151),
128    BFieldElement::new(9231805330431056952),
129    BFieldElement::new(9252272755288523725),
130    BFieldElement::new(10014268662326746219),
131    BFieldElement::new(15565031632950843234),
132    BFieldElement::new(1209725273521819323),
133    BFieldElement::new(6024642864597845108),
134];
135
136/// The defining, first column of the (circulant) MDS matrix.
137/// Derived from the SHA-256 hash of the ASCII string “Tip5” by dividing the digest into 16-bit
138/// chunks.
139pub const MDS_MATRIX_FIRST_COLUMN: [i64; STATE_SIZE] = [
140    61402, 1108, 28750, 33823, 7454, 43244, 53865, 12034, 56951, 27521, 41351, 40901, 12021, 59689,
141    26798, 17845,
142];
143
144#[derive(
145    Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, GetSize, BFieldCodec, Arbitrary,
146)]
147pub struct Tip5 {
148    pub state: [BFieldElement; STATE_SIZE],
149}
150
151impl Tip5 {
152    #[inline]
153    pub const fn new(domain: Domain) -> Self {
154        use Domain::*;
155
156        let mut state = [BFieldElement::ZERO; STATE_SIZE];
157
158        match domain {
159            VariableLength => (),
160            FixedLength => {
161                let mut i = RATE;
162                while i < STATE_SIZE {
163                    state[i] = BFieldElement::ONE;
164                    i += 1;
165                }
166            }
167        }
168
169        Self { state }
170    }
171
172    #[inline]
173    fn split_and_lookup(element: &mut BFieldElement) {
174        // let value = element.value();
175        let mut bytes = element.raw_bytes();
176
177        for i in 0..8 {
178            // bytes[i] = Self::offset_fermat_cube_map(bytes[i].into()) as u8;
179            bytes[i] = LOOKUP_TABLE[bytes[i] as usize];
180        }
181
182        *element = BFieldElement::from_raw_bytes(&bytes);
183    }
184
185    #[inline(always)]
186    fn mds_generated(&mut self) {
187        let mut lo: [u64; STATE_SIZE] = [0; STATE_SIZE];
188        let mut hi: [u64; STATE_SIZE] = [0; STATE_SIZE];
189        for i in 0..STATE_SIZE {
190            let b = self.state[i].raw_u64();
191            hi[i] = b >> 32;
192            lo[i] = b & 0xffffffffu64;
193        }
194
195        lo = Self::generated_function(lo);
196        hi = Self::generated_function(hi);
197
198        for r in 0..STATE_SIZE {
199            let s = (lo[r] >> 4) as u128 + ((hi[r] as u128) << 28);
200
201            let s_hi = (s >> 64) as u64;
202            let s_lo = s as u64;
203
204            let (res, over) = s_lo.overflowing_add(s_hi * 0xffffffffu64);
205
206            self.state[r] =
207                BFieldElement::from_raw_u64(if over { res + 0xffffffffu64 } else { res });
208        }
209    }
210
211    #[inline(always)]
212    fn generated_function(input: [u64; STATE_SIZE]) -> [u64; STATE_SIZE] {
213        let node_34 = input[0].wrapping_add(input[8]);
214        let node_38 = input[4].wrapping_add(input[12]);
215        let node_36 = input[2].wrapping_add(input[10]);
216        let node_40 = input[6].wrapping_add(input[14]);
217        let node_35 = input[1].wrapping_add(input[9]);
218        let node_39 = input[5].wrapping_add(input[13]);
219        let node_37 = input[3].wrapping_add(input[11]);
220        let node_41 = input[7].wrapping_add(input[15]);
221        let node_50 = node_34.wrapping_add(node_38);
222        let node_52 = node_36.wrapping_add(node_40);
223        let node_51 = node_35.wrapping_add(node_39);
224        let node_53 = node_37.wrapping_add(node_41);
225        let node_160 = input[0].wrapping_sub(input[8]);
226        let node_161 = input[1].wrapping_sub(input[9]);
227        let node_165 = input[5].wrapping_sub(input[13]);
228        let node_163 = input[3].wrapping_sub(input[11]);
229        let node_167 = input[7].wrapping_sub(input[15]);
230        let node_162 = input[2].wrapping_sub(input[10]);
231        let node_166 = input[6].wrapping_sub(input[14]);
232        let node_164 = input[4].wrapping_sub(input[12]);
233        let node_58 = node_50.wrapping_add(node_52);
234        let node_59 = node_51.wrapping_add(node_53);
235        let node_90 = node_34.wrapping_sub(node_38);
236        let node_91 = node_35.wrapping_sub(node_39);
237        let node_93 = node_37.wrapping_sub(node_41);
238        let node_92 = node_36.wrapping_sub(node_40);
239        let node_64 = node_58.wrapping_add(node_59).wrapping_mul(524757);
240        let node_67 = node_58.wrapping_sub(node_59).wrapping_mul(52427);
241        let node_71 = node_50.wrapping_sub(node_52);
242        let node_72 = node_51.wrapping_sub(node_53);
243        let node_177 = node_161.wrapping_add(node_165);
244        let node_179 = node_163.wrapping_add(node_167);
245        let node_178 = node_162.wrapping_add(node_166);
246        let node_176 = node_160.wrapping_add(node_164);
247        let node_69 = node_64.wrapping_add(node_67);
248        let node_397 = node_71
249            .wrapping_mul(18446744073709525744)
250            .wrapping_sub(node_72.wrapping_mul(53918));
251        let node_1857 = node_90.wrapping_mul(395512);
252        let node_99 = node_91.wrapping_add(node_93);
253        let node_1865 = node_91.wrapping_mul(18446744073709254400);
254        let node_1869 = node_93.wrapping_mul(179380);
255        let node_1873 = node_92.wrapping_mul(18446744073709509368);
256        let node_1879 = node_160.wrapping_mul(35608);
257        let node_185 = node_161.wrapping_add(node_163);
258        let node_1915 = node_161.wrapping_mul(18446744073709340312);
259        let node_1921 = node_163.wrapping_mul(18446744073709494992);
260        let node_1927 = node_162.wrapping_mul(18446744073709450808);
261        let node_228 = node_165.wrapping_add(node_167);
262        let node_1939 = node_165.wrapping_mul(18446744073709420056);
263        let node_1945 = node_167.wrapping_mul(18446744073709505128);
264        let node_1951 = node_166.wrapping_mul(216536);
265        let node_1957 = node_164.wrapping_mul(18446744073709515080);
266        let node_70 = node_64.wrapping_sub(node_67);
267        let node_702 = node_71
268            .wrapping_mul(53918)
269            .wrapping_add(node_72.wrapping_mul(18446744073709525744));
270        let node_1961 = node_90.wrapping_mul(18446744073709254400);
271        let node_1963 = node_91.wrapping_mul(395512);
272        let node_1965 = node_92.wrapping_mul(179380);
273        let node_1967 = node_93.wrapping_mul(18446744073709509368);
274        let node_1970 = node_160.wrapping_mul(18446744073709340312);
275        let node_1973 = node_161.wrapping_mul(35608);
276        let node_1982 = node_162.wrapping_mul(18446744073709494992);
277        let node_1985 = node_163.wrapping_mul(18446744073709450808);
278        let node_1988 = node_166.wrapping_mul(18446744073709505128);
279        let node_1991 = node_167.wrapping_mul(216536);
280        let node_1994 = node_164.wrapping_mul(18446744073709420056);
281        let node_1997 = node_165.wrapping_mul(18446744073709515080);
282        let node_98 = node_90.wrapping_add(node_92);
283        let node_184 = node_160.wrapping_add(node_162);
284        let node_227 = node_164.wrapping_add(node_166);
285        let node_86 = node_69.wrapping_add(node_397);
286        let node_403 = node_1857.wrapping_sub(
287            node_99
288                .wrapping_mul(18446744073709433780)
289                .wrapping_sub(node_1865)
290                .wrapping_sub(node_1869)
291                .wrapping_add(node_1873),
292        );
293        let node_271 = node_177.wrapping_add(node_179);
294        let node_1891 = node_177.wrapping_mul(18446744073709208752);
295        let node_1897 = node_179.wrapping_mul(18446744073709448504);
296        let node_1903 = node_178.wrapping_mul(115728);
297        let node_1909 = node_185.wrapping_mul(18446744073709283688);
298        let node_1933 = node_228.wrapping_mul(18446744073709373568);
299        let node_88 = node_70.wrapping_add(node_702);
300        let node_708 = node_1961
301            .wrapping_add(node_1963)
302            .wrapping_sub(node_1965.wrapping_add(node_1967));
303        let node_1976 = node_178.wrapping_mul(18446744073709448504);
304        let node_1979 = node_179.wrapping_mul(115728);
305        let node_87 = node_69.wrapping_sub(node_397);
306        let node_897 = node_1865
307            .wrapping_add(node_98.wrapping_mul(353264))
308            .wrapping_sub(node_1857)
309            .wrapping_sub(node_1873)
310            .wrapping_sub(node_1869);
311        let node_2007 = node_184.wrapping_mul(18446744073709486416);
312        let node_2013 = node_227.wrapping_mul(180000);
313        let node_89 = node_70.wrapping_sub(node_702);
314        let node_1077 = node_98
315            .wrapping_mul(18446744073709433780)
316            .wrapping_add(node_99.wrapping_mul(353264))
317            .wrapping_sub(node_1961.wrapping_add(node_1963))
318            .wrapping_sub(node_1965.wrapping_add(node_1967));
319        let node_2020 = node_184.wrapping_mul(18446744073709283688);
320        let node_2023 = node_185.wrapping_mul(18446744073709486416);
321        let node_2026 = node_227.wrapping_mul(18446744073709373568);
322        let node_2029 = node_228.wrapping_mul(180000);
323        let node_2035 = node_176.wrapping_mul(18446744073709550688);
324        let node_2038 = node_176.wrapping_mul(18446744073709208752);
325        let node_2041 = node_177.wrapping_mul(18446744073709550688);
326        let node_270 = node_176.wrapping_add(node_178);
327        let node_152 = node_86.wrapping_add(node_403);
328        let node_412 = node_1879.wrapping_sub(
329            node_271
330                .wrapping_mul(18446744073709105640)
331                .wrapping_sub(node_1891)
332                .wrapping_sub(node_1897)
333                .wrapping_add(node_1903)
334                .wrapping_sub(
335                    node_1909
336                        .wrapping_sub(node_1915)
337                        .wrapping_sub(node_1921)
338                        .wrapping_add(node_1927),
339                )
340                .wrapping_sub(
341                    node_1933
342                        .wrapping_sub(node_1939)
343                        .wrapping_sub(node_1945)
344                        .wrapping_add(node_1951),
345                )
346                .wrapping_add(node_1957),
347        );
348        let node_154 = node_88.wrapping_add(node_708);
349        let node_717 = node_1970.wrapping_add(node_1973).wrapping_sub(
350            node_1976
351                .wrapping_add(node_1979)
352                .wrapping_sub(node_1982.wrapping_add(node_1985))
353                .wrapping_sub(node_1988.wrapping_add(node_1991))
354                .wrapping_add(node_1994.wrapping_add(node_1997)),
355        );
356        let node_156 = node_87.wrapping_add(node_897);
357        let node_906 = node_1915
358            .wrapping_add(node_2007)
359            .wrapping_sub(node_1879)
360            .wrapping_sub(node_1927)
361            .wrapping_sub(
362                node_1897
363                    .wrapping_sub(node_1921)
364                    .wrapping_sub(node_1945)
365                    .wrapping_add(
366                        node_1939
367                            .wrapping_add(node_2013)
368                            .wrapping_sub(node_1957)
369                            .wrapping_sub(node_1951),
370                    ),
371            );
372        let node_158 = node_89.wrapping_add(node_1077);
373        let node_1086 = node_2020
374            .wrapping_add(node_2023)
375            .wrapping_sub(node_1970.wrapping_add(node_1973))
376            .wrapping_sub(node_1982.wrapping_add(node_1985))
377            .wrapping_sub(
378                node_2026
379                    .wrapping_add(node_2029)
380                    .wrapping_sub(node_1994.wrapping_add(node_1997))
381                    .wrapping_sub(node_1988.wrapping_add(node_1991)),
382            );
383        let node_153 = node_86.wrapping_sub(node_403);
384        let node_1237 = node_1909
385            .wrapping_sub(node_1915)
386            .wrapping_sub(node_1921)
387            .wrapping_add(node_1927)
388            .wrapping_add(node_2035)
389            .wrapping_sub(node_1879)
390            .wrapping_sub(node_1957)
391            .wrapping_sub(
392                node_1933
393                    .wrapping_sub(node_1939)
394                    .wrapping_sub(node_1945)
395                    .wrapping_add(node_1951),
396            );
397        let node_155 = node_88.wrapping_sub(node_708);
398        let node_1375 = node_1982
399            .wrapping_add(node_1985)
400            .wrapping_add(node_2038.wrapping_add(node_2041))
401            .wrapping_sub(node_1970.wrapping_add(node_1973))
402            .wrapping_sub(node_1994.wrapping_add(node_1997))
403            .wrapping_sub(node_1988.wrapping_add(node_1991));
404        let node_157 = node_87.wrapping_sub(node_897);
405        let node_1492 = node_1921
406            .wrapping_add(
407                node_1891
408                    .wrapping_add(node_270.wrapping_mul(114800))
409                    .wrapping_sub(node_2035)
410                    .wrapping_sub(node_1903),
411            )
412            .wrapping_sub(
413                node_1915
414                    .wrapping_add(node_2007)
415                    .wrapping_sub(node_1879)
416                    .wrapping_sub(node_1927),
417            )
418            .wrapping_sub(
419                node_1939
420                    .wrapping_add(node_2013)
421                    .wrapping_sub(node_1957)
422                    .wrapping_sub(node_1951),
423            )
424            .wrapping_sub(node_1945);
425        let node_159 = node_89.wrapping_sub(node_1077);
426        let node_1657 = node_270
427            .wrapping_mul(18446744073709105640)
428            .wrapping_add(node_271.wrapping_mul(114800))
429            .wrapping_sub(node_2038.wrapping_add(node_2041))
430            .wrapping_sub(node_1976.wrapping_add(node_1979))
431            .wrapping_sub(
432                node_2020
433                    .wrapping_add(node_2023)
434                    .wrapping_sub(node_1970.wrapping_add(node_1973))
435                    .wrapping_sub(node_1982.wrapping_add(node_1985)),
436            )
437            .wrapping_sub(
438                node_2026
439                    .wrapping_add(node_2029)
440                    .wrapping_sub(node_1994.wrapping_add(node_1997))
441                    .wrapping_sub(node_1988.wrapping_add(node_1991)),
442            );
443
444        [
445            node_152.wrapping_add(node_412),
446            node_154.wrapping_add(node_717),
447            node_156.wrapping_add(node_906),
448            node_158.wrapping_add(node_1086),
449            node_153.wrapping_add(node_1237),
450            node_155.wrapping_add(node_1375),
451            node_157.wrapping_add(node_1492),
452            node_159.wrapping_add(node_1657),
453            node_152.wrapping_sub(node_412),
454            node_154.wrapping_sub(node_717),
455            node_156.wrapping_sub(node_906),
456            node_158.wrapping_sub(node_1086),
457            node_153.wrapping_sub(node_1237),
458            node_155.wrapping_sub(node_1375),
459            node_157.wrapping_sub(node_1492),
460            node_159.wrapping_sub(node_1657),
461        ]
462    }
463
464    #[inline(always)]
465    fn sbox_layer(&mut self) {
466        for i in 0..NUM_SPLIT_AND_LOOKUP {
467            Self::split_and_lookup(&mut self.state[i]);
468        }
469
470        for i in NUM_SPLIT_AND_LOOKUP..STATE_SIZE {
471            let sq = self.state[i] * self.state[i];
472            let qu = sq * sq;
473            self.state[i] *= sq * qu;
474        }
475    }
476
477    #[inline(always)]
478    fn round(&mut self, round_index: usize) {
479        self.sbox_layer();
480        self.mds_generated();
481        for i in 0..STATE_SIZE {
482            self.state[i] += ROUND_CONSTANTS[round_index * STATE_SIZE + i];
483        }
484    }
485
486    #[inline(always)]
487    pub fn permutation(&mut self) {
488        for i in 0..NUM_ROUNDS {
489            self.round(i);
490        }
491    }
492
493    /// Functionally equivalent to [`permutation`](Self::permutation). Returns the trace of
494    /// applying the permutation; that is, the initial state of the sponge as well as its state
495    /// after each round.
496    pub fn trace(&mut self) -> [[BFieldElement; STATE_SIZE]; 1 + NUM_ROUNDS] {
497        let mut trace = [[BFieldElement::ZERO; STATE_SIZE]; 1 + NUM_ROUNDS];
498
499        trace[0] = self.state;
500        for i in 0..NUM_ROUNDS {
501            self.round(i);
502            trace[1 + i] = self.state;
503        }
504
505        trace
506    }
507
508    /// Hash 10 [`BFieldElement`]s.
509    ///
510    /// There is no input-padding because the input length is fixed.
511    ///
512    /// When you want to hash together two [`Digest`]s, use [`Self::hash_pair`]
513    /// instead. In some rare cases you do want to hash a fixed-length string
514    /// of individual [`BFieldElement`]s, which is why this function is exposed.
515    ///
516    /// See also: [`Self::hash_pair`], [`Self::hash`], [`Self::hash_varlen`].
517    pub fn hash_10(input: &[BFieldElement; 10]) -> [BFieldElement; Digest::LEN] {
518        let mut sponge = Self::new(Domain::FixedLength);
519
520        // absorb once
521        sponge.state[..10].copy_from_slice(input);
522
523        sponge.permutation();
524
525        // squeeze once
526        sponge.state[..Digest::LEN].try_into().unwrap()
527    }
528
529    /// Hash two [`Digest`]s together.
530    ///
531    /// This function is syntax sugar for calling [`Self::hash_10`] on the
532    /// concatenation of the digests' values.
533    ///
534    /// See also: [`Self::hash_10`], [`Self::hash`], [`Self::hash_varlen`].
535    pub fn hash_pair(left: Digest, right: Digest) -> Digest {
536        let mut sponge = Self::new(Domain::FixedLength);
537        sponge.state[..Digest::LEN].copy_from_slice(&left.values());
538        sponge.state[Digest::LEN..2 * Digest::LEN].copy_from_slice(&right.values());
539
540        sponge.permutation();
541
542        let digest_values = sponge.state[..Digest::LEN].try_into().unwrap();
543        Digest::new(digest_values)
544    }
545
546    /// Hash an object based on its [`BFieldCodec`]-encoding.
547    ///
548    /// Thin wrapper around [`hash_varlen`](Self::hash_varlen).
549    ///
550    /// See also: [`Self::hash_10`], [`Self::hash_pair`], [`Self::hash_varlen`].
551    pub fn hash<T: BFieldCodec>(value: &T) -> Digest {
552        Self::hash_varlen(&value.encode())
553    }
554
555    /// Hash a variable-length sequence of [`BFieldElement`].
556    ///
557    /// This function pads the input as its length is variable.
558    ///
559    /// Note that [`Self::hash_varlen`] and [`Self::hash_10`] are different
560    /// functions, even when the input to the former, after padding, agrees with
561    /// the input to the latter. The difference comes from the initial value of
562    /// the capacity-part of the state, which in the case of variable-length
563    /// hashing is all-ones but in the case of fixed-length hashing is
564    /// all-zeroes.
565    ///
566    /// Prefer [`Self::hash`] whenever an object is being hashed whose type
567    /// implements [`BFieldCodec`]. However, such an object is not always
568    /// available, which is why this function is exposed.
569    ///
570    /// See also: [`Self::hash_10`], [`Self::hash_pair`], [`Self::hash`].
571    //
572    // - Apply the correct padding
573    // - [Sponge::pad_and_absorb_all()]
574    // - Read the digest from the resulting state.
575    pub fn hash_varlen(input: &[BFieldElement]) -> Digest {
576        let mut sponge = Self::init();
577        sponge.pad_and_absorb_all(input);
578        let produce: [BFieldElement; Digest::LEN] =
579            (&sponge.state[..Digest::LEN]).try_into().unwrap();
580
581        Digest::new(produce)
582    }
583
584    /// Produce `num_indices` random integer values in the range `[0, upper_bound)`. The
585    /// `upper_bound` must be a power of 2.
586    ///
587    /// This method uses von Neumann rejection sampling.
588    /// Specifically, if the top 32 bits of a BFieldElement are all ones, then the bottom 32 bits
589    /// are not uniformly distributed, and so they are dropped. This method invokes squeeze until
590    /// enough uniform u32s have been sampled.
591    pub fn sample_indices(&mut self, upper_bound: u32, num_indices: usize) -> Vec<u32> {
592        debug_assert!(upper_bound.is_power_of_two());
593        let mut indices = vec![];
594        let mut squeezed_elements = vec![];
595        while indices.len() != num_indices {
596            if squeezed_elements.is_empty() {
597                squeezed_elements = self.squeeze().into_iter().rev().collect_vec();
598            }
599            let element = squeezed_elements.pop().unwrap();
600            if element != BFieldElement::new(BFieldElement::MAX) {
601                indices.push(element.value() as u32 % upper_bound);
602            }
603        }
604        indices
605    }
606
607    /// Produce `num_elements` random [`XFieldElement`] values.
608    ///
609    /// If `num_elements` is not divisible by [`RATE`][rate], spill the remaining elements of the
610    /// last [`squeeze`][Sponge::squeeze].
611    ///
612    /// [rate]: Sponge::RATE
613    pub fn sample_scalars(&mut self, num_elements: usize) -> Vec<XFieldElement> {
614        let num_squeezes = (num_elements * EXTENSION_DEGREE).div_ceil(Self::RATE);
615        debug_assert!(
616            num_elements * EXTENSION_DEGREE <= num_squeezes * Self::RATE,
617            "need {} elements but getting {}",
618            num_elements * EXTENSION_DEGREE,
619            num_squeezes * Self::RATE
620        );
621        (0..num_squeezes)
622            .flat_map(|_| self.squeeze())
623            .collect_vec()
624            .chunks(3)
625            .take(num_elements)
626            .map(|elem| XFieldElement::new([elem[0], elem[1], elem[2]]))
627            .collect()
628    }
629}
630
631impl Sponge for Tip5 {
632    const RATE: usize = RATE;
633
634    fn init() -> Self {
635        Self::new(Domain::VariableLength)
636    }
637
638    fn absorb(&mut self, input: [BFieldElement; RATE]) {
639        self.state[..RATE]
640            .iter_mut()
641            .zip_eq(&input)
642            .for_each(|(a, &b)| *a = b);
643
644        self.permutation();
645    }
646
647    fn squeeze(&mut self) -> [BFieldElement; RATE] {
648        let produce: [BFieldElement; RATE] = (&self.state[..RATE]).try_into().unwrap();
649        self.permutation();
650
651        produce
652    }
653}
654
655impl Hasher for Tip5 {
656    fn finish(&self) -> u64 {
657        self.state[0].value()
658    }
659
660    fn write(&mut self, bytes: &[u8]) {
661        let bfield_elements = bytes.chunks(BFieldElement::BYTES).map(|chunk| {
662            let mut buffer = [0u8; BFieldElement::BYTES];
663            buffer[..chunk.len()].copy_from_slice(chunk);
664            BFieldElement::new(u64::from_le_bytes(buffer))
665        });
666
667        for chunk in bfield_elements.chunks(Tip5::RATE).into_iter() {
668            let mut buffer = [BFieldElement::ZERO; Tip5::RATE];
669            for (buffer_elem, chunk_elem) in buffer.iter_mut().zip(chunk) {
670                *buffer_elem = chunk_elem;
671            }
672            self.absorb(buffer)
673        }
674    }
675}
676
677#[cfg(test)]
678#[cfg_attr(coverage_nightly, coverage(off))]
679pub(crate) mod tests {
680    use std::hash::Hash;
681    use std::ops::Mul;
682
683    use prop::sample::size_range;
684    use proptest::prelude::*;
685    use proptest_arbitrary_interop::arb;
686    use rand::Rng;
687    use rand::RngCore;
688    use rayon::prelude::IntoParallelIterator;
689    use rayon::prelude::ParallelIterator;
690    use test_strategy::proptest;
691
692    use super::*;
693    use crate::math::other::random_elements;
694    use crate::math::x_field_element::XFieldElement;
695
696    impl Tip5 {
697        pub(crate) fn randomly_seeded() -> Self {
698            let mut sponge = Self::init();
699            let mut rng = rand::rng();
700            sponge.absorb(rng.random());
701            sponge
702        }
703
704        fn mds_cyclomul(&mut self) {
705            let mut result = [BFieldElement::ZERO; STATE_SIZE];
706
707            let mut lo: [i64; STATE_SIZE] = [0; STATE_SIZE];
708            let mut hi: [i64; STATE_SIZE] = [0; STATE_SIZE];
709            for (i, b) in self.state.iter().enumerate() {
710                hi[i] = (b.raw_u64() >> 32) as i64;
711                lo[i] = (b.raw_u64() as u32) as i64;
712            }
713
714            lo = Self::fast_cyclomul16(lo, MDS_MATRIX_FIRST_COLUMN);
715            hi = Self::fast_cyclomul16(hi, MDS_MATRIX_FIRST_COLUMN);
716
717            for r in 0..STATE_SIZE {
718                let s = lo[r] as u128 + ((hi[r] as u128) << 32);
719                let s_hi = (s >> 64) as u64;
720                let s_lo = s as u64;
721                let z = (s_hi << 32) - s_hi;
722                let (res, over) = s_lo.overflowing_add(z);
723
724                result[r] = BFieldElement::from_raw_u64(
725                    res.wrapping_add(0u32.wrapping_sub(over as u32) as u64),
726                );
727            }
728            self.state = result;
729        }
730
731        fn fast_cyclomul16(f: [i64; 16], g: [i64; 16]) -> [i64; 16] {
732            const N: usize = 8;
733            let mut ff_lo = [0i64; N];
734            let mut gg_lo = [0i64; N];
735            let mut ff_hi = [0i64; N];
736            let mut gg_hi = [0i64; N];
737            for i in 0..N {
738                ff_lo[i] = f[i] + f[i + N];
739                ff_hi[i] = f[i] - f[i + N];
740                gg_lo[i] = g[i] + g[i + N];
741                gg_hi[i] = g[i] - g[i + N];
742            }
743
744            let hh_lo = Self::fast_cyclomul8(ff_lo, gg_lo);
745            let hh_hi = Self::complex_negacyclomul8(ff_hi, gg_hi);
746
747            let mut hh = [0i64; 2 * N];
748            for i in 0..N {
749                hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
750                hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
751            }
752
753            hh
754        }
755
756        fn fast_cyclomul8(f: [i64; 8], g: [i64; 8]) -> [i64; 8] {
757            const N: usize = 4;
758            let mut ff_lo = [0i64; N];
759            let mut gg_lo = [0i64; N];
760            let mut ff_hi = [0i64; N];
761            let mut gg_hi = [0i64; N];
762            for i in 0..N {
763                ff_lo[i] = f[i] + f[i + N];
764                ff_hi[i] = f[i] - f[i + N];
765                gg_lo[i] = g[i] + g[i + N];
766                gg_hi[i] = g[i] - g[i + N];
767            }
768
769            let hh_lo = Self::fast_cyclomul4(ff_lo, gg_lo);
770            let hh_hi = Self::complex_negacyclomul4(ff_hi, gg_hi);
771
772            let mut hh = [0i64; 2 * N];
773            for i in 0..N {
774                hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
775                hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
776            }
777
778            hh
779        }
780
781        fn fast_cyclomul4(f: [i64; 4], g: [i64; 4]) -> [i64; 4] {
782            const N: usize = 2;
783            let mut ff_lo = [0i64; N];
784            let mut gg_lo = [0i64; N];
785            let mut ff_hi = [0i64; N];
786            let mut gg_hi = [0i64; N];
787            for i in 0..N {
788                ff_lo[i] = f[i] + f[i + N];
789                ff_hi[i] = f[i] - f[i + N];
790                gg_lo[i] = g[i] + g[i + N];
791                gg_hi[i] = g[i] - g[i + N];
792            }
793
794            let hh_lo = Self::fast_cyclomul2(ff_lo, gg_lo);
795            let hh_hi = Self::complex_negacyclomul2(ff_hi, gg_hi);
796
797            let mut hh = [0i64; 2 * N];
798            for i in 0..N {
799                hh[i] = (hh_lo[i] + hh_hi[i]) >> 1;
800                hh[i + N] = (hh_lo[i] - hh_hi[i]) >> 1;
801            }
802
803            hh
804        }
805
806        fn fast_cyclomul2(f: [i64; 2], g: [i64; 2]) -> [i64; 2] {
807            let ff_lo = f[0] + f[1];
808            let ff_hi = f[0] - f[1];
809            let gg_lo = g[0] + g[1];
810            let gg_hi = g[0] - g[1];
811
812            let hh_lo = ff_lo * gg_lo;
813            let hh_hi = ff_hi * gg_hi;
814
815            let mut hh = [0i64; 2];
816            hh[0] = (hh_lo + hh_hi) >> 1;
817            hh[1] = (hh_lo - hh_hi) >> 1;
818
819            hh
820        }
821
822        fn complex_negacyclomul8(f: [i64; 8], g: [i64; 8]) -> [i64; 8] {
823            const N: usize = 4;
824
825            let mut f0 = [(0i64, 0i64); N];
826            let mut g0 = [(0i64, 0i64); N];
827
828            for i in 0..N {
829                f0[i] = (f[i], -f[N + i]);
830                g0[i] = (g[i], -g[N + i]);
831            }
832
833            let h0 = Self::complex_karatsuba4(f0, g0);
834
835            let mut h = [0i64; 3 * N - 1];
836            for i in 0..(2 * N - 1) {
837                h[i] += h0[i].0;
838                h[i + N] -= h0[i].1;
839            }
840
841            let mut hh = [0i64; 2 * N];
842            for i in 0..(2 * N) {
843                hh[i] += h[i];
844            }
845            for i in (2 * N)..(3 * N - 1) {
846                hh[i - 2 * N] -= h[i];
847            }
848
849            hh
850        }
851
852        fn complex_negacyclomul4(f: [i64; 4], g: [i64; 4]) -> [i64; 4] {
853            const N: usize = 2;
854
855            let mut f0 = [(0i64, 0i64); N];
856            let mut g0 = [(0i64, 0i64); N];
857
858            for i in 0..N {
859                f0[i] = (f[i], -f[N + i]);
860                g0[i] = (g[i], -g[N + i]);
861            }
862
863            let h0 = Self::complex_karatsuba2(f0, g0);
864
865            let mut h = [0i64; 4 * N - 1];
866            for i in 0..(2 * N - 1) {
867                h[i] += h0[i].0;
868                h[i + N] -= h0[i].1;
869            }
870
871            let mut hh = [0i64; 2 * N];
872            for i in 0..(2 * N) {
873                hh[i] += h[i];
874            }
875            for i in (2 * N)..(4 * N - 1) {
876                hh[i - 2 * N] -= h[i];
877            }
878
879            hh
880        }
881
882        fn complex_negacyclomul2(f: [i64; 2], g: [i64; 2]) -> [i64; 2] {
883            let f0 = (f[0], -f[1]);
884            let g0 = (g[0], -g[1]);
885
886            let h0 = Self::complex_product(f0, g0);
887
888            [h0.0, -h0.1]
889        }
890
891        #[inline(always)]
892        fn complex_karatsuba4(f: [(i64, i64); 4], g: [(i64, i64); 4]) -> [(i64, i64); 7] {
893            const N: usize = 2;
894
895            let ff = Self::complex_sum::<2>(f[..N].try_into().unwrap(), f[N..].try_into().unwrap());
896            let gg = Self::complex_sum::<2>(g[..N].try_into().unwrap(), g[N..].try_into().unwrap());
897
898            let lo =
899                Self::complex_karatsuba2(f[..N].try_into().unwrap(), g[..N].try_into().unwrap());
900            let hi =
901                Self::complex_karatsuba2(f[N..].try_into().unwrap(), g[N..].try_into().unwrap());
902
903            let li = Self::complex_diff::<3>(
904                Self::complex_karatsuba2(ff, gg),
905                Self::complex_sum::<3>(lo, hi),
906            );
907
908            let mut result = [(0i64, 0i64); 4 * N - 1];
909            for i in 0..(2 * N - 1) {
910                result[i].0 = lo[i].0;
911                result[i].1 = lo[i].1;
912            }
913            for i in 0..(2 * N - 1) {
914                result[N + i].0 += li[i].0;
915                result[N + i].1 += li[i].1;
916            }
917            for i in 0..(2 * N - 1) {
918                result[2 * N + i].0 += hi[i].0;
919                result[2 * N + i].1 += hi[i].1;
920            }
921
922            result
923        }
924
925        fn complex_karatsuba2(f: [(i64, i64); 2], g: [(i64, i64); 2]) -> [(i64, i64); 3] {
926            const N: usize = 1;
927
928            let ff = (f[0].0 + f[1].0, f[0].1 + f[1].1);
929            let gg = (g[0].0 + g[1].0, g[0].1 + g[1].1);
930
931            let lo = Self::complex_product(f[0], g[0]);
932            let hi = Self::complex_product(f[1], g[1]);
933
934            let ff_times_gg = Self::complex_product(ff, gg);
935            let lo_plus_hi = (lo.0 + hi.0, lo.1 + hi.1);
936
937            let li = (ff_times_gg.0 - lo_plus_hi.0, ff_times_gg.1 - lo_plus_hi.1);
938
939            let mut result = [(0i64, 0i64); 4 * N - 1];
940            result[0].0 += lo.0;
941            result[0].1 += lo.1;
942            result[N].0 += li.0;
943            result[N].1 += li.1;
944            result[2 * N].0 += hi.0;
945            result[2 * N].1 += hi.1;
946
947            result
948        }
949
950        fn complex_product(f: (i64, i64), g: (i64, i64)) -> (i64, i64) {
951            // don't karatsuba; this is faster
952            (f.0 * g.0 - f.1 * g.1, f.0 * g.1 + f.1 * g.0)
953        }
954
955        fn complex_sum<const N: usize>(f: [(i64, i64); N], g: [(i64, i64); N]) -> [(i64, i64); N] {
956            let mut h = [(0i64, 0i64); N];
957            for i in 0..N {
958                h[i].0 = f[i].0 + g[i].0;
959                h[i].1 = f[i].1 + g[i].1;
960            }
961            h
962        }
963
964        fn complex_diff<const N: usize>(f: [(i64, i64); N], g: [(i64, i64); N]) -> [(i64, i64); N] {
965            let mut h = [(0i64, 0i64); N];
966            for i in 0..N {
967                h[i].0 = f[i].0 - g[i].0;
968                h[i].1 = f[i].1 - g[i].1;
969            }
970            h
971        }
972
973        fn offset_fermat_cube_map(x: u16) -> u16 {
974            let xx = (x + 1) as u64;
975            let xxx = xx * xx * xx;
976            ((xxx + 256) % 257) as u16
977        }
978    }
979
980    #[test]
981    fn get_size_test() {
982        assert_eq!(
983            STATE_SIZE * BFieldElement::ZERO.get_size(),
984            Tip5::randomly_seeded().get_size()
985        );
986    }
987
988    #[test]
989    fn lookup_table_is_correct() {
990        let table: [u8; 256] = (0..256)
991            .map(|t| Tip5::offset_fermat_cube_map(t as u16) as u8)
992            .collect_vec()
993            .try_into()
994            .unwrap();
995
996        println!(
997            "Entire lookup table:\n{}",
998            table.iter().map(|t| format!("{t:02x}")).join(", ")
999        );
1000
1001        (0_usize..256).for_each(|i| {
1002            assert_eq!(
1003                LOOKUP_TABLE[i], table[i],
1004                "Lookup tables must agree at every index, including index {i}."
1005            )
1006        });
1007    }
1008
1009    #[test]
1010    fn round_constants_are_correct() {
1011        let to_int = |bytes: &[u8]| {
1012            bytes
1013                .iter()
1014                .take(16)
1015                .enumerate()
1016                .map(|(i, b)| (*b as u128) << (8 * i))
1017                .sum::<u128>()
1018        };
1019        let round_constants = (0..NUM_ROUNDS * STATE_SIZE)
1020            .map(|i| ["Tip5".to_string().as_bytes(), &[i as u8]].concat())
1021            .map(|bytes| blake3::hash(&bytes))
1022            .map(|hash| *hash.as_bytes())
1023            .map(|bytes| to_int(&bytes))
1024            .map(|i| (i % BFieldElement::P as u128) as u64)
1025            .map(BFieldElement::from_raw_u64)
1026            .collect_vec();
1027
1028        println!(
1029            "In case you changed something, here are all round constants:\n{}",
1030            round_constants.iter().map(|c| format!("{c}")).join(", ")
1031        );
1032
1033        (0_usize..NUM_ROUNDS * STATE_SIZE).for_each(|i| {
1034            assert_eq!(
1035                ROUND_CONSTANTS[i], round_constants[i],
1036                "Round constants must agree at every index, including index {i}."
1037            )
1038        });
1039    }
1040
1041    #[test]
1042    fn test_fermat_cube_map_is_permutation() {
1043        let mut touched = [false; 256];
1044        for i in 0..256 {
1045            touched[Tip5::offset_fermat_cube_map(i) as usize] = true;
1046        }
1047        assert!(touched.iter().all(|t| *t));
1048        assert_eq!(Tip5::offset_fermat_cube_map(0), 0);
1049        assert_eq!(Tip5::offset_fermat_cube_map(255), 255);
1050    }
1051
1052    #[test]
1053    fn calculate_differential_uniformity() {
1054        // cargo test calculate_differential_uniformity -- --include-ignored --nocapture
1055        // addition-differential
1056        let count_satisfiers_fermat = |a, b| {
1057            (0..(1 << 8))
1058                .map(|x| {
1059                    u16::from(
1060                        (256 + Tip5::offset_fermat_cube_map((x + a) & 0xff)
1061                            - Tip5::offset_fermat_cube_map(x))
1062                            & 0xff
1063                            == b,
1064                    )
1065                })
1066                .sum()
1067        };
1068        let du_fermat: u16 = (1..256)
1069            .into_par_iter()
1070            .map(|a| {
1071                (1..256)
1072                    .map(|b| count_satisfiers_fermat(a, b))
1073                    .max()
1074                    .unwrap()
1075            })
1076            .max()
1077            .unwrap();
1078        println!("additive differential uniformity for fermat cube map: {du_fermat}");
1079
1080        // bitwise-differential
1081        let count_satisfiers_fermat_bitwise = |a: u16, b: u16| {
1082            (0..(1 << 8))
1083                .map(|x| {
1084                    u16::from(
1085                        (Tip5::offset_fermat_cube_map(x ^ a) ^ Tip5::offset_fermat_cube_map(x))
1086                            == b,
1087                    )
1088                })
1089                .sum::<u16>()
1090        };
1091        for a in 1..256 {
1092            for b in 1..256 {
1093                let num_satisfiers = count_satisfiers_fermat_bitwise(a, b);
1094                if num_satisfiers == 256 {
1095                    println!("a: {a}, b: {b} -> 256 satisfiers");
1096                }
1097            }
1098        }
1099        let du_fermat_bitwise: u16 = (1..256)
1100            .into_par_iter()
1101            .map(|a| {
1102                (1..256)
1103                    .map(|b| count_satisfiers_fermat_bitwise(a, b))
1104                    .max()
1105                    .unwrap()
1106            })
1107            .max()
1108            .unwrap();
1109        println!("bitwise differential uniformity for fermat cube map: {du_fermat_bitwise}");
1110    }
1111
1112    #[test]
1113    fn calculate_approximation_quality() {
1114        let mut fermat_cubed = [0u16; 65536];
1115        let mut bfield_cubed = [0u16; 65536];
1116        for i in 0..65536 {
1117            let cubed = (i as u64) * (i as u64) * (i as u64);
1118            fermat_cubed[i] = (cubed % 65537) as u16;
1119            bfield_cubed[i] = (cubed & 0xffff) as u16;
1120        }
1121        let equal_count = fermat_cubed
1122            .iter()
1123            .zip(bfield_cubed.iter())
1124            .filter(|(a, b)| a == b)
1125            .count();
1126        println!("agreement with low-degree function: {equal_count}");
1127    }
1128
1129    #[test]
1130    fn hash10_test_vectors() {
1131        let mut preimage = [BFieldElement::ZERO; RATE];
1132        let mut digest: [BFieldElement; Digest::LEN];
1133        for i in 0..6 {
1134            digest = Tip5::hash_10(&preimage);
1135            println!(
1136                "{:?} -> {:?}",
1137                preimage.iter().map(|b| b.value()).collect_vec(),
1138                digest.iter().map(|b| b.value()).collect_vec()
1139            );
1140            preimage[i..Digest::LEN + i].copy_from_slice(&digest);
1141        }
1142        digest = Tip5::hash_10(&preimage);
1143        println!(
1144            "{:?} -> {:?}",
1145            preimage.iter().map(|b| b.value()).collect_vec(),
1146            digest.iter().map(|b| b.value()).collect_vec()
1147        );
1148        let final_digest = [
1149            10869784347448351760,
1150            1853783032222938415,
1151            6856460589287344822,
1152            17178399545409290325,
1153            7650660984651717733,
1154        ]
1155        .map(BFieldElement::new);
1156        assert_eq!(
1157            digest,
1158            final_digest,
1159            "expected: {:?}\nbut got: {:?}",
1160            final_digest.map(|d| d.value()),
1161            digest.map(|d| d.value()),
1162        )
1163    }
1164
1165    #[test]
1166    fn hash_varlen_test_vectors() {
1167        let mut digest_sum = [BFieldElement::ZERO; Digest::LEN];
1168        for i in 0..20 {
1169            let preimage = (0..i).map(BFieldElement::new).collect_vec();
1170            let digest = Tip5::hash_varlen(&preimage);
1171            println!(
1172                "{:?} -> {:?}",
1173                preimage.iter().map(|b| b.value()).collect_vec(),
1174                digest.values().iter().map(|b| b.value()).collect_vec()
1175            );
1176            digest_sum
1177                .iter_mut()
1178                .zip(digest.values().iter())
1179                .for_each(|(s, d)| *s += *d);
1180        }
1181        println!(
1182            "sum of digests: {:?}",
1183            digest_sum.iter().map(|b| b.value()).collect_vec()
1184        );
1185        let expected_sum = [
1186            7610004073009036015,
1187            5725198067541094245,
1188            4721320565792709122,
1189            1732504843634706218,
1190            259800783350288362,
1191        ]
1192        .map(BFieldElement::new);
1193        assert_eq!(
1194            expected_sum,
1195            digest_sum,
1196            "expected: {:?}\nbut got: {:?}",
1197            expected_sum.map(|s| s.value()),
1198            digest_sum.map(|s| s.value())
1199        );
1200    }
1201
1202    fn manual_hash_varlen(preimage: &[BFieldElement]) -> Digest {
1203        let mut sponge = Tip5::init();
1204        sponge.pad_and_absorb_all(preimage);
1205        let squeeze_result = sponge.squeeze();
1206
1207        Digest::new((&squeeze_result[..Digest::LEN]).try_into().unwrap())
1208    }
1209
1210    #[test]
1211    fn hash_var_len_equivalence_corner_cases() {
1212        for preimage_length in 0..=11 {
1213            let preimage = vec![BFieldElement::new(42); preimage_length];
1214            let hash_varlen_digest = Tip5::hash_varlen(&preimage);
1215
1216            let digest_through_pad_squeeze_absorb = manual_hash_varlen(&preimage);
1217            assert_eq!(digest_through_pad_squeeze_absorb, hash_varlen_digest);
1218        }
1219    }
1220
1221    #[proptest]
1222    fn hash_var_len_equivalence(#[strategy(arb())] preimage: Vec<BFieldElement>) {
1223        let hash_varlen_digest = Tip5::hash_varlen(&preimage);
1224        let digest_through_pad_squeeze_absorb = manual_hash_varlen(&preimage);
1225        prop_assert_eq!(digest_through_pad_squeeze_absorb, hash_varlen_digest);
1226    }
1227
1228    #[test]
1229    fn test_linearity_of_mds() {
1230        type SpongeState = [BFieldElement; STATE_SIZE];
1231
1232        let mds_procedure = |state| {
1233            let mut sponge = Tip5 { state };
1234            sponge.mds_cyclomul();
1235            sponge.state
1236        };
1237
1238        let a: BFieldElement = random_elements(1)[0];
1239        let b: BFieldElement = random_elements(1)[0];
1240
1241        let mul_procedure = |u: SpongeState, v: SpongeState| -> SpongeState {
1242            let mul_result = u.iter().zip(&v).map(|(&uu, &vv)| a * uu + b * vv);
1243            mul_result.collect_vec().try_into().unwrap()
1244        };
1245
1246        let u: SpongeState = random_elements(STATE_SIZE).try_into().unwrap();
1247        let v: SpongeState = random_elements(STATE_SIZE).try_into().unwrap();
1248        let w = mul_procedure(u, v);
1249
1250        let u = mds_procedure(u);
1251        let v = mds_procedure(v);
1252        let w = mds_procedure(w);
1253
1254        let w_ = mul_procedure(u, v);
1255
1256        assert_eq!(w, w_);
1257    }
1258
1259    #[test]
1260    fn test_mds_circulancy() {
1261        let mut sponge = Tip5::init();
1262        sponge.state = [BFieldElement::ZERO; STATE_SIZE];
1263        sponge.state[0] = BFieldElement::ONE;
1264
1265        sponge.mds_generated();
1266
1267        let mut mat_first_row = [BFieldElement::ZERO; STATE_SIZE];
1268        mat_first_row[0] = sponge.state[0];
1269        for (i, first_row_elem) in mat_first_row.iter_mut().enumerate().skip(1) {
1270            *first_row_elem = sponge.state[STATE_SIZE - i];
1271        }
1272
1273        println!(
1274            "mds matrix first row: {:?}",
1275            mat_first_row.map(|b| b.value())
1276        );
1277
1278        let initial_state: [BFieldElement; STATE_SIZE] =
1279            random_elements(STATE_SIZE).try_into().unwrap();
1280
1281        let mut mv = [BFieldElement::ZERO; STATE_SIZE];
1282        for i in 0..STATE_SIZE {
1283            for j in 0..STATE_SIZE {
1284                mv[i] += mat_first_row[(STATE_SIZE - i + j) % STATE_SIZE] * initial_state[j];
1285            }
1286        }
1287
1288        let mut sponge_2 = Tip5::init();
1289        sponge_2.state = initial_state;
1290        sponge_2.mds_generated();
1291
1292        assert_eq!(sponge_2.state, mv);
1293    }
1294
1295    #[test]
1296    fn test_complex_karatsuba() {
1297        const N: usize = 4;
1298        let mut f = [(0i64, 0i64); N];
1299        let mut g = [(0i64, 0i64); N];
1300        for i in 0..N {
1301            f[i].0 = i as i64;
1302            g[i].0 = 1;
1303            f[i].1 = 1;
1304            g[i].1 = i as i64;
1305        }
1306
1307        let h0 = Tip5::complex_karatsuba4(f, g);
1308        let h1 = [(0, 1), (0, 2), (0, 4), (0, 8), (0, 13), (0, 14), (0, 10)];
1309
1310        assert_eq!(h0, h1);
1311    }
1312
1313    #[test]
1314    fn test_complex_product() {
1315        let mut rng = rand::rng();
1316        let mut random_small_i64 = || (rng.next_u32() % (1 << 16)) as i64;
1317        for _ in 0..1000 {
1318            let f = (random_small_i64(), random_small_i64());
1319            let g = (random_small_i64(), random_small_i64());
1320            let h0 = Tip5::complex_product(f, g);
1321            let h1 = (f.0 * g.0 - f.1 * g.1, f.0 * g.1 + f.1 * g.0);
1322            assert_eq!(h1, h0);
1323        }
1324    }
1325
1326    #[test]
1327    fn sample_scalars_test() {
1328        let mut sponge = Tip5::randomly_seeded();
1329        let mut product = XFieldElement::ONE;
1330        for amount in 0..=4 {
1331            let scalars = sponge.sample_scalars(amount);
1332            assert_eq!(amount, scalars.len());
1333            product *= scalars
1334                .into_iter()
1335                .fold(XFieldElement::ONE, XFieldElement::mul);
1336        }
1337        assert_ne!(product, XFieldElement::ZERO); // false failure with prob ~2^{-192}
1338    }
1339
1340    #[test]
1341    fn test_mds_agree() {
1342        let mut rng = rand::rng();
1343        let initial_state: [BFieldElement; STATE_SIZE] = (0..STATE_SIZE)
1344            .map(|_| BFieldElement::new(rng.random_range(0..10)))
1345            .collect_vec()
1346            .try_into()
1347            .unwrap();
1348
1349        let mut sponge_cyclomut = Tip5 {
1350            state: initial_state,
1351        };
1352        let mut sponge_generated = Tip5 {
1353            state: initial_state,
1354        };
1355
1356        sponge_cyclomut.mds_cyclomul();
1357        sponge_generated.mds_generated();
1358
1359        assert_eq!(
1360            sponge_cyclomut,
1361            sponge_generated,
1362            "cyclomul =/= generated\n{}\n{}",
1363            sponge_cyclomut.state.into_iter().join(","),
1364            sponge_generated.state.into_iter().join(",")
1365        );
1366    }
1367
1368    #[test]
1369    fn tip5_hasher_trait_snapshot_test() {
1370        let mut hasher = Tip5::init();
1371        let data = b"hello world";
1372        hasher.write(data);
1373        assert_eq!(hasher.finish(), 2267905471610932299);
1374    }
1375
1376    #[proptest]
1377    fn tip5_hasher_consumes_small_data(#[filter(!#bytes.is_empty())] bytes: Vec<u8>) {
1378        let mut hasher = Tip5::init();
1379        bytes.hash(&mut hasher);
1380
1381        prop_assert_ne!(Tip5::init().finish(), hasher.finish());
1382    }
1383
1384    #[proptest]
1385    fn appending_small_data_to_big_data_changes_tip5_hash(
1386        #[any(size_range(2_000..8_000).lift())] big_data: Vec<u8>,
1387        #[filter(!#small_data.is_empty())] small_data: Vec<u8>,
1388    ) {
1389        let mut hasher = Tip5::init();
1390        big_data.hash(&mut hasher);
1391        let big_data_hash = hasher.finish();
1392
1393        // finish doesn't terminate the hasher; see it's documentation
1394        small_data.hash(&mut hasher);
1395        let all_data_hash = hasher.finish();
1396
1397        prop_assert_ne!(big_data_hash, all_data_hash);
1398    }
1399
1400    #[proptest]
1401    fn tip5_trace_starts_with_initial_state_and_is_equivalent_to_permutation(
1402        #[strategy(arb())] mut tip5: Tip5,
1403    ) {
1404        let [first, .., last] = tip5.clone().trace();
1405        prop_assert_eq!(first, tip5.state);
1406
1407        tip5.permutation();
1408        prop_assert_eq!(last, tip5.state);
1409    }
1410}