miden_crypto/word/
mod.rs

1//! A [Word] type used in the Miden protocol and associated utilities.
2
3use alloc::{string::String, vec::Vec};
4use core::{
5    cmp::Ordering,
6    fmt::Display,
7    hash::{Hash, Hasher},
8    ops::{Deref, DerefMut, Index, IndexMut, Range},
9    slice,
10};
11
12use thiserror::Error;
13
14const WORD_SIZE_FELT: usize = 4;
15const WORD_SIZE_BYTES: usize = 32;
16
17use p3_field::integers::QuotientMap;
18
19use super::{Felt, ZERO};
20use crate::{
21    field::{PrimeCharacteristicRing, PrimeField64},
22    rand::Randomizable,
23    utils::{
24        ByteReader, ByteWriter, Deserializable, DeserializationError, HexParseError, Serializable,
25        bytes_to_hex_string, hex_to_bytes,
26    },
27};
28
29mod lexicographic;
30pub use lexicographic::LexicographicWord;
31
32#[cfg(test)]
33mod tests;
34
35// WORD
36// ================================================================================================
37
38/// A unit of data consisting of 4 field elements.
39#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
40#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
41#[cfg_attr(feature = "serde", serde(into = "String", try_from = "&str"))]
42pub struct Word([Felt; WORD_SIZE_FELT]);
43
44impl Word {
45    /// The serialized size of the word in bytes.
46    pub const SERIALIZED_SIZE: usize = WORD_SIZE_BYTES;
47
48    /// Creates a new [`Word`] from the given field elements.
49    pub const fn new(value: [Felt; WORD_SIZE_FELT]) -> Self {
50        Self(value)
51    }
52
53    /// Parses a hex string into a new [`Word`].
54    ///
55    /// The input must contain valid hex prefixed with `0x`. The input after the prefix
56    /// must contain between 0 and 64 characters (inclusive).
57    ///
58    /// The input is interpreted to have little-endian byte ordering. Nibbles are interpreted
59    /// to have big-endian ordering so that "0x10" represents Felt::new(16), not Felt::new(1).
60    ///
61    /// This function is usually used via the `word!` macro.
62    ///
63    /// ```
64    /// use miden_crypto::{Felt, Word, word};
65    /// let word = word!("0x1000000000000000200000000000000030000000000000004000000000000000");
66    /// assert_eq!(word, Word::new([Felt::new(16), Felt::new(32), Felt::new(48), Felt::new(64)]));
67    /// ```
68    pub const fn parse(hex: &str) -> Result<Self, &'static str> {
69        const fn parse_hex_digit(digit: u8) -> Result<u8, &'static str> {
70            match digit {
71                b'0'..=b'9' => Ok(digit - b'0'),
72                b'A'..=b'F' => Ok(digit - b'A' + 0x0a),
73                b'a'..=b'f' => Ok(digit - b'a' + 0x0a),
74                _ => Err("Invalid hex character"),
75            }
76        }
77        // Enforce and skip the '0x' prefix.
78        let hex_bytes = match hex.as_bytes() {
79            [b'0', b'x', rest @ ..] => rest,
80            _ => return Err("Hex string must have a \"0x\" prefix"),
81        };
82
83        if hex_bytes.len() > 64 {
84            return Err("Hex string has more than 64 characters");
85        }
86
87        let mut felts = [0u64; 4];
88        let mut i = 0;
89        while i < hex_bytes.len() {
90            let hex_digit = match parse_hex_digit(hex_bytes[i]) {
91                // SAFETY: u8 cast to u64 is safe. We cannot use u64::from in const context so we
92                // are forced to cast.
93                Ok(v) => v as u64,
94                Err(e) => return Err(e),
95            };
96
97            // This digit's nibble offset within the felt. We need to invert the nibbles per
98            // byte to ensure little-endian ordering i.e. ABCD -> BADC.
99            let inibble = if i.is_multiple_of(2) {
100                (i + 1) % 16
101            } else {
102                (i - 1) % 16
103            };
104
105            let value = hex_digit << (inibble * 4);
106            felts[i / 2 / 8] += value;
107
108            i += 1;
109        }
110
111        // Ensure each felt is within bounds as `Felt::new` silently wraps around.
112        // This matches the behavior of `Word::try_from(String)`.
113        let mut idx = 0;
114        while idx < felts.len() {
115            if felts[idx] >= Felt::ORDER_U64 {
116                return Err("Felt overflow");
117            }
118            idx += 1;
119        }
120
121        Ok(Self::new([
122            Felt::new(felts[0]),
123            Felt::new(felts[1]),
124            Felt::new(felts[2]),
125            Felt::new(felts[3]),
126        ]))
127    }
128
129    /// Returns a new [Word] consisting of four ZERO elements.
130    pub const fn empty() -> Self {
131        Self([Felt::ZERO; WORD_SIZE_FELT])
132    }
133
134    /// Returns true if the word consists of four ZERO elements.
135    pub fn is_empty(&self) -> bool {
136        self.0[0] == Felt::ZERO
137            && self.0[1] == Felt::ZERO
138            && self.0[2] == Felt::ZERO
139            && self.0[3] == Felt::ZERO
140    }
141
142    /// Returns the word as a slice of field elements.
143    pub fn as_elements(&self) -> &[Felt] {
144        self.as_ref()
145    }
146
147    /// Returns the word as a byte array.
148    pub fn as_bytes(&self) -> [u8; WORD_SIZE_BYTES] {
149        let mut result = [0; WORD_SIZE_BYTES];
150
151        result[..8].copy_from_slice(&self.0[0].as_canonical_u64().to_le_bytes());
152        result[8..16].copy_from_slice(&self.0[1].as_canonical_u64().to_le_bytes());
153        result[16..24].copy_from_slice(&self.0[2].as_canonical_u64().to_le_bytes());
154        result[24..].copy_from_slice(&self.0[3].as_canonical_u64().to_le_bytes());
155
156        result
157    }
158
159    /// Returns an iterator over the elements of multiple words.
160    pub(crate) fn words_as_elements_iter<'a, I>(words: I) -> impl Iterator<Item = &'a Felt>
161    where
162        I: Iterator<Item = &'a Self>,
163    {
164        words.flat_map(|d| d.0.iter())
165    }
166
167    /// Returns all elements of multiple words as a slice.
168    pub fn words_as_elements(words: &[Self]) -> &[Felt] {
169        let p = words.as_ptr();
170        let len = words.len() * WORD_SIZE_FELT;
171        unsafe { slice::from_raw_parts(p as *const Felt, len) }
172    }
173
174    /// Returns hexadecimal representation of this word prefixed with `0x`.
175    pub fn to_hex(&self) -> String {
176        bytes_to_hex_string(self.as_bytes())
177    }
178
179    /// Returns internal elements of this word as a vector.
180    pub fn to_vec(&self) -> Vec<Felt> {
181        self.0.to_vec()
182    }
183}
184
185impl Hash for Word {
186    fn hash<H: Hasher>(&self, state: &mut H) {
187        state.write(&self.as_bytes());
188    }
189}
190
191impl Deref for Word {
192    type Target = [Felt; WORD_SIZE_FELT];
193
194    fn deref(&self) -> &Self::Target {
195        &self.0
196    }
197}
198
199impl DerefMut for Word {
200    fn deref_mut(&mut self) -> &mut Self::Target {
201        &mut self.0
202    }
203}
204
205impl Index<usize> for Word {
206    type Output = Felt;
207
208    fn index(&self, index: usize) -> &Self::Output {
209        &self.0[index]
210    }
211}
212
213impl IndexMut<usize> for Word {
214    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
215        &mut self.0[index]
216    }
217}
218
219impl Index<Range<usize>> for Word {
220    type Output = [Felt];
221
222    fn index(&self, index: Range<usize>) -> &Self::Output {
223        &self.0[index]
224    }
225}
226
227impl IndexMut<Range<usize>> for Word {
228    fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
229        &mut self.0[index]
230    }
231}
232
233impl Ord for Word {
234    fn cmp(&self, other: &Self) -> Ordering {
235        // Compare the canonical u64 representation of both elements.
236        //
237        // It will iterate the elements and will return the first computation different than
238        // `Equal`. Otherwise, the ordering is equal.
239        //
240        // We use `as_canonical_u64()` to ensure we're comparing the actual field element values
241        // in their canonical form (that is, `x in [0,p)`). P3's Goldilocks field uses unreduced
242        // representation (not Montgomery form), meaning internal values may be in [0, 2^64) even
243        // though the field order is p = 2^64 - 2^32 + 1. This method canonicalizes to [0, p).
244        //
245        // We must iterate over and compare each element individually. A simple bytestring
246        // comparison would be inappropriate because the `Word`s are represented in
247        // "lexicographical" order.
248        self.0
249            .iter()
250            .map(Felt::as_canonical_u64)
251            .zip(other.0.iter().map(Felt::as_canonical_u64))
252            .fold(Ordering::Equal, |ord, (a, b)| match ord {
253                Ordering::Equal => a.cmp(&b),
254                _ => ord,
255            })
256    }
257}
258
259impl PartialOrd for Word {
260    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
261        Some(self.cmp(other))
262    }
263}
264
265impl Display for Word {
266    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
267        write!(f, "{}", self.to_hex())
268    }
269}
270
271impl Randomizable for Word {
272    const VALUE_SIZE: usize = WORD_SIZE_BYTES;
273
274    fn from_random_bytes(bytes: &[u8]) -> Option<Self> {
275        let bytes_array: Option<[u8; 32]> = bytes.try_into().ok();
276        if let Some(bytes_array) = bytes_array {
277            Self::try_from(bytes_array).ok()
278        } else {
279            None
280        }
281    }
282}
283
284// CONVERSIONS: FROM WORD
285// ================================================================================================
286
287/// Errors that can occur when working with a [Word].
288#[derive(Debug, Error)]
289pub enum WordError {
290    /// Hex-encoded field elements parsed are invalid.
291    #[error("hex encoded values of a word are invalid")]
292    HexParse(#[from] HexParseError),
293    /// Field element conversion failed due to invalid value.
294    #[error("failed to convert to field element: {0}")]
295    InvalidFieldElement(String),
296    /// Failed to convert a slice to an array of expected length.
297    #[error("invalid input length: expected {1} {0}, but received {2}")]
298    InvalidInputLength(&'static str, usize, usize),
299    /// Failed to convert the word's field elements to the specified type.
300    #[error("failed to convert the word's field elements to type {0}")]
301    TypeConversion(&'static str),
302}
303
304impl TryFrom<&Word> for [bool; WORD_SIZE_FELT] {
305    type Error = WordError;
306
307    fn try_from(value: &Word) -> Result<Self, Self::Error> {
308        (*value).try_into()
309    }
310}
311
312impl TryFrom<Word> for [bool; WORD_SIZE_FELT] {
313    type Error = WordError;
314
315    fn try_from(value: Word) -> Result<Self, Self::Error> {
316        fn to_bool(v: u64) -> Option<bool> {
317            if v <= 1 { Some(v == 1) } else { None }
318        }
319
320        Ok([
321            to_bool(value.0[0].as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
322            to_bool(value.0[1].as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
323            to_bool(value.0[2].as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
324            to_bool(value.0[3].as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
325        ])
326    }
327}
328
329impl TryFrom<&Word> for [u8; WORD_SIZE_FELT] {
330    type Error = WordError;
331
332    fn try_from(value: &Word) -> Result<Self, Self::Error> {
333        (*value).try_into()
334    }
335}
336
337impl TryFrom<Word> for [u8; WORD_SIZE_FELT] {
338    type Error = WordError;
339
340    fn try_from(value: Word) -> Result<Self, Self::Error> {
341        Ok([
342            value.0[0]
343                .as_canonical_u64()
344                .try_into()
345                .map_err(|_| WordError::TypeConversion("u8"))?,
346            value.0[1]
347                .as_canonical_u64()
348                .try_into()
349                .map_err(|_| WordError::TypeConversion("u8"))?,
350            value.0[2]
351                .as_canonical_u64()
352                .try_into()
353                .map_err(|_| WordError::TypeConversion("u8"))?,
354            value.0[3]
355                .as_canonical_u64()
356                .try_into()
357                .map_err(|_| WordError::TypeConversion("u8"))?,
358        ])
359    }
360}
361
362impl TryFrom<&Word> for [u16; WORD_SIZE_FELT] {
363    type Error = WordError;
364
365    fn try_from(value: &Word) -> Result<Self, Self::Error> {
366        (*value).try_into()
367    }
368}
369
370impl TryFrom<Word> for [u16; WORD_SIZE_FELT] {
371    type Error = WordError;
372
373    fn try_from(value: Word) -> Result<Self, Self::Error> {
374        Ok([
375            value.0[0]
376                .as_canonical_u64()
377                .try_into()
378                .map_err(|_| WordError::TypeConversion("u16"))?,
379            value.0[1]
380                .as_canonical_u64()
381                .try_into()
382                .map_err(|_| WordError::TypeConversion("u16"))?,
383            value.0[2]
384                .as_canonical_u64()
385                .try_into()
386                .map_err(|_| WordError::TypeConversion("u16"))?,
387            value.0[3]
388                .as_canonical_u64()
389                .try_into()
390                .map_err(|_| WordError::TypeConversion("u16"))?,
391        ])
392    }
393}
394
395impl TryFrom<&Word> for [u32; WORD_SIZE_FELT] {
396    type Error = WordError;
397
398    fn try_from(value: &Word) -> Result<Self, Self::Error> {
399        (*value).try_into()
400    }
401}
402
403impl TryFrom<Word> for [u32; WORD_SIZE_FELT] {
404    type Error = WordError;
405
406    fn try_from(value: Word) -> Result<Self, Self::Error> {
407        Ok([
408            value.0[0]
409                .as_canonical_u64()
410                .try_into()
411                .map_err(|_| WordError::TypeConversion("u32"))?,
412            value.0[1]
413                .as_canonical_u64()
414                .try_into()
415                .map_err(|_| WordError::TypeConversion("u32"))?,
416            value.0[2]
417                .as_canonical_u64()
418                .try_into()
419                .map_err(|_| WordError::TypeConversion("u32"))?,
420            value.0[3]
421                .as_canonical_u64()
422                .try_into()
423                .map_err(|_| WordError::TypeConversion("u32"))?,
424        ])
425    }
426}
427
428impl From<&Word> for [u64; WORD_SIZE_FELT] {
429    fn from(value: &Word) -> Self {
430        (*value).into()
431    }
432}
433
434impl From<Word> for [u64; WORD_SIZE_FELT] {
435    fn from(value: Word) -> Self {
436        [
437            value.0[0].as_canonical_u64(),
438            value.0[1].as_canonical_u64(),
439            value.0[2].as_canonical_u64(),
440            value.0[3].as_canonical_u64(),
441        ]
442    }
443}
444
445impl From<&Word> for [Felt; WORD_SIZE_FELT] {
446    fn from(value: &Word) -> Self {
447        (*value).into()
448    }
449}
450
451impl From<Word> for [Felt; WORD_SIZE_FELT] {
452    fn from(value: Word) -> Self {
453        value.0
454    }
455}
456
457impl From<&Word> for [u8; WORD_SIZE_BYTES] {
458    fn from(value: &Word) -> Self {
459        (*value).into()
460    }
461}
462
463impl From<Word> for [u8; WORD_SIZE_BYTES] {
464    fn from(value: Word) -> Self {
465        value.as_bytes()
466    }
467}
468
469impl From<&Word> for String {
470    /// The returned string starts with `0x`.
471    fn from(value: &Word) -> Self {
472        (*value).into()
473    }
474}
475
476impl From<Word> for String {
477    /// The returned string starts with `0x`.
478    fn from(value: Word) -> Self {
479        value.to_hex()
480    }
481}
482
483// CONVERSIONS: TO WORD
484// ================================================================================================
485
486impl From<&[bool; WORD_SIZE_FELT]> for Word {
487    fn from(value: &[bool; WORD_SIZE_FELT]) -> Self {
488        (*value).into()
489    }
490}
491
492impl From<[bool; WORD_SIZE_FELT]> for Word {
493    fn from(value: [bool; WORD_SIZE_FELT]) -> Self {
494        [value[0] as u32, value[1] as u32, value[2] as u32, value[3] as u32].into()
495    }
496}
497
498impl From<&[u8; WORD_SIZE_FELT]> for Word {
499    fn from(value: &[u8; WORD_SIZE_FELT]) -> Self {
500        (*value).into()
501    }
502}
503
504impl From<[u8; WORD_SIZE_FELT]> for Word {
505    fn from(value: [u8; WORD_SIZE_FELT]) -> Self {
506        Self([
507            Felt::from_u8(value[0]),
508            Felt::from_u8(value[1]),
509            Felt::from_u8(value[2]),
510            Felt::from_u8(value[3]),
511        ])
512    }
513}
514
515impl From<&[u16; WORD_SIZE_FELT]> for Word {
516    fn from(value: &[u16; WORD_SIZE_FELT]) -> Self {
517        (*value).into()
518    }
519}
520
521impl From<[u16; WORD_SIZE_FELT]> for Word {
522    fn from(value: [u16; WORD_SIZE_FELT]) -> Self {
523        Self([
524            Felt::from_u16(value[0]),
525            Felt::from_u16(value[1]),
526            Felt::from_u16(value[2]),
527            Felt::from_u16(value[3]),
528        ])
529    }
530}
531
532impl From<&[u32; WORD_SIZE_FELT]> for Word {
533    fn from(value: &[u32; WORD_SIZE_FELT]) -> Self {
534        (*value).into()
535    }
536}
537
538impl From<[u32; WORD_SIZE_FELT]> for Word {
539    fn from(value: [u32; WORD_SIZE_FELT]) -> Self {
540        Self([
541            Felt::from_u32(value[0]),
542            Felt::from_u32(value[1]),
543            Felt::from_u32(value[2]),
544            Felt::from_u32(value[3]),
545        ])
546    }
547}
548
549impl TryFrom<&[u64; WORD_SIZE_FELT]> for Word {
550    type Error = WordError;
551
552    fn try_from(value: &[u64; WORD_SIZE_FELT]) -> Result<Self, WordError> {
553        (*value).try_into()
554    }
555}
556
557impl TryFrom<[u64; WORD_SIZE_FELT]> for Word {
558    type Error = WordError;
559
560    fn try_from(value: [u64; WORD_SIZE_FELT]) -> Result<Self, WordError> {
561        let err = || WordError::InvalidFieldElement("value >= field modulus".into());
562        Ok(Self([
563            Felt::from_canonical_checked(value[0]).ok_or_else(err)?,
564            Felt::from_canonical_checked(value[1]).ok_or_else(err)?,
565            Felt::from_canonical_checked(value[2]).ok_or_else(err)?,
566            Felt::from_canonical_checked(value[3]).ok_or_else(err)?,
567        ]))
568    }
569}
570
571impl From<&[Felt; WORD_SIZE_FELT]> for Word {
572    fn from(value: &[Felt; WORD_SIZE_FELT]) -> Self {
573        Self(*value)
574    }
575}
576
577impl From<[Felt; WORD_SIZE_FELT]> for Word {
578    fn from(value: [Felt; WORD_SIZE_FELT]) -> Self {
579        Self(value)
580    }
581}
582
583impl TryFrom<&[u8; WORD_SIZE_BYTES]> for Word {
584    type Error = WordError;
585
586    fn try_from(value: &[u8; WORD_SIZE_BYTES]) -> Result<Self, Self::Error> {
587        (*value).try_into()
588    }
589}
590
591impl TryFrom<[u8; WORD_SIZE_BYTES]> for Word {
592    type Error = WordError;
593
594    fn try_from(value: [u8; WORD_SIZE_BYTES]) -> Result<Self, Self::Error> {
595        // Note: the input length is known, the conversion from slice to array must succeed so the
596        // `unwrap`s below are safe
597        let a = u64::from_le_bytes(value[0..8].try_into().unwrap());
598        let b = u64::from_le_bytes(value[8..16].try_into().unwrap());
599        let c = u64::from_le_bytes(value[16..24].try_into().unwrap());
600        let d = u64::from_le_bytes(value[24..32].try_into().unwrap());
601
602        let err = || WordError::InvalidFieldElement("value >= field modulus".into());
603        let a: Felt = Felt::from_canonical_checked(a).ok_or_else(err)?;
604        let b: Felt = Felt::from_canonical_checked(b).ok_or_else(err)?;
605        let c: Felt = Felt::from_canonical_checked(c).ok_or_else(err)?;
606        let d: Felt = Felt::from_canonical_checked(d).ok_or_else(err)?;
607
608        Ok(Word([a, b, c, d]))
609    }
610}
611
612impl TryFrom<&[u8]> for Word {
613    type Error = WordError;
614
615    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
616        let value: [u8; WORD_SIZE_BYTES] = value
617            .try_into()
618            .map_err(|_| WordError::InvalidInputLength("bytes", WORD_SIZE_BYTES, value.len()))?;
619        value.try_into()
620    }
621}
622
623impl TryFrom<&[Felt]> for Word {
624    type Error = WordError;
625
626    fn try_from(value: &[Felt]) -> Result<Self, Self::Error> {
627        let value: [Felt; WORD_SIZE_FELT] = value
628            .try_into()
629            .map_err(|_| WordError::InvalidInputLength("elements", WORD_SIZE_FELT, value.len()))?;
630        Ok(value.into())
631    }
632}
633
634impl TryFrom<&str> for Word {
635    type Error = WordError;
636
637    /// Expects the string to start with `0x`.
638    fn try_from(value: &str) -> Result<Self, Self::Error> {
639        hex_to_bytes::<WORD_SIZE_BYTES>(value)
640            .map_err(WordError::HexParse)
641            .and_then(Word::try_from)
642    }
643}
644
645impl TryFrom<String> for Word {
646    type Error = WordError;
647
648    /// Expects the string to start with `0x`.
649    fn try_from(value: String) -> Result<Self, Self::Error> {
650        value.as_str().try_into()
651    }
652}
653
654impl TryFrom<&String> for Word {
655    type Error = WordError;
656
657    /// Expects the string to start with `0x`.
658    fn try_from(value: &String) -> Result<Self, Self::Error> {
659        value.as_str().try_into()
660    }
661}
662
663// SERIALIZATION / DESERIALIZATION
664// ================================================================================================
665
666impl Serializable for Word {
667    fn write_into<W: ByteWriter>(&self, target: &mut W) {
668        target.write_bytes(&self.as_bytes());
669    }
670
671    fn get_size_hint(&self) -> usize {
672        Self::SERIALIZED_SIZE
673    }
674}
675
676impl Deserializable for Word {
677    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
678        let mut inner: [Felt; WORD_SIZE_FELT] = [ZERO; WORD_SIZE_FELT];
679        for inner in inner.iter_mut() {
680            let e = source.read_u64()?;
681            if e >= Felt::ORDER_U64 {
682                return Err(DeserializationError::InvalidValue(String::from(
683                    "value not in the appropriate range",
684                )));
685            }
686            *inner = Felt::new(e);
687        }
688
689        Ok(Self(inner))
690    }
691}
692
693// ITERATORS
694// ================================================================================================
695impl IntoIterator for Word {
696    type Item = Felt;
697    type IntoIter = <[Felt; 4] as IntoIterator>::IntoIter;
698
699    fn into_iter(self) -> Self::IntoIter {
700        self.0.into_iter()
701    }
702}
703
704// MACROS
705// ================================================================================================
706
707/// Construct a new [Word](super::Word) from a hex value.
708///
709/// Expects a '0x' prefixed hex string followed by up to 64 hex digits.
710#[macro_export]
711macro_rules! word {
712    ($hex:expr) => {{
713        let word: Word = match $crate::word::Word::parse($hex) {
714            Ok(v) => v,
715            Err(e) => panic!("{}", e),
716        };
717
718        word
719    }};
720}
721
722// ARBITRARY (proptest)
723// ================================================================================================
724
725#[cfg(any(test, feature = "testing"))]
726mod arbitrary {
727    use proptest::prelude::*;
728
729    use super::{Felt, Word};
730
731    impl Arbitrary for Word {
732        type Parameters = ();
733        type Strategy = BoxedStrategy<Self>;
734
735        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
736            prop::collection::vec(any::<u64>(), 4)
737                .prop_map(|vals| {
738                    Word::new([
739                        Felt::new(vals[0]),
740                        Felt::new(vals[1]),
741                        Felt::new(vals[2]),
742                        Felt::new(vals[3]),
743                    ])
744                })
745                .no_shrink()
746                .boxed()
747        }
748    }
749}