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#[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 pub a: Felt,
49 pub b: Felt,
50 pub c: Felt,
51 pub d: Felt,
52 }
62
63const _: () = {
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 pub const NUM_ELEMENTS: usize = 4;
84
85 pub const SERIALIZED_SIZE: usize = 32;
87
88 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 pub const fn into_elements(self) -> [Felt; Self::NUM_ELEMENTS] {
96 [self.a, self.b, self.c, self.d]
97 }
98
99 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 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 #[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 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 Ok(v) => v as u64,
167 Err(e) => return Err(e),
168 };
169
170 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 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 pub const fn empty() -> Self {
204 Self::new([Felt::ZERO; Self::NUM_ELEMENTS])
205 }
206
207 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 pub fn as_elements(&self) -> &[Felt] {
218 self.as_elements_array()
219 }
220
221 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 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 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 pub fn to_hex(&self) -> String {
250 bytes_to_hex_string(self.as_bytes())
251 }
252
253 pub fn to_vec(&self) -> Vec<Felt> {
255 self.as_elements().to_vec()
256 }
257
258 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 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#[derive(Debug, Error)]
366pub enum WordError {
367 #[error("hex encoded values of a word are invalid")]
369 HexParse(#[from] crate::utils::HexParseError),
370 #[error("failed to convert to field element: {0}")]
372 InvalidFieldElement(String),
373 #[error("invalid input length: expected {1} {0}, but received {2}")]
375 InvalidInputLength(&'static str, usize, usize),
376 #[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 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 fn from(value: Word) -> Self {
521 value.to_hex()
522 }
523}
524
525impl 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 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 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 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 fn try_from(value: &String) -> Result<Self, Self::Error> {
704 value.as_str().try_into()
705 }
706}
707
708#[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
744impl 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#[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#[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}