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