Skip to main content

miden_field/word/
mod.rs

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