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