1use 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#[derive(Default, Copy, Clone, Eq, PartialEq)]
36#[repr(C)]
37#[cfg_attr(all(target_family = "wasm", miden), repr(align(16)))]
38pub struct Word {
39 pub a: Felt,
41 pub b: Felt,
42 pub c: Felt,
43 pub d: Felt,
44 }
54
55const _: () = {
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 pub const NUM_ELEMENTS: usize = 4;
76
77 pub const SERIALIZED_SIZE: usize = 32;
79
80 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 pub const fn into_elements(self) -> [Felt; Self::NUM_ELEMENTS] {
88 [self.a, self.b, self.c, self.d]
89 }
90
91 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 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 #[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 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 Ok(v) => v as u64,
159 Err(e) => return Err(e),
160 };
161
162 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 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 pub const fn empty() -> Self {
196 Self::new([Felt::ZERO; Self::NUM_ELEMENTS])
197 }
198
199 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 pub fn as_elements(&self) -> &[Felt] {
210 self.as_elements_array()
211 }
212
213 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 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 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 pub fn to_hex(&self) -> String {
242 bytes_to_hex_string(self.as_bytes())
243 }
244
245 pub fn to_vec(&self) -> Vec<Felt> {
247 self.as_elements().to_vec()
248 }
249
250 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 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#[derive(Debug, Error)]
358pub enum WordError {
359 #[error("hex encoded values of a word are invalid")]
361 HexParse(#[from] crate::utils::HexParseError),
362 #[error("failed to convert to field element: {0}")]
364 InvalidFieldElement(String),
365 #[error("invalid input length: expected {1} {0}, but received {2}")]
367 InvalidInputLength(&'static str, usize, usize),
368 #[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 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 fn from(value: Word) -> Self {
513 value.to_hex()
514 }
515}
516
517impl 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 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 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 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 fn try_from(value: &String) -> Result<Self, Self::Error> {
696 value.as_str().try_into()
697 }
698}
699
700#[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
736impl 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#[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#[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}