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