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 ops::{Deref, DerefMut, Index, IndexMut, Range},
10 slice,
11};
12
13#[cfg(not(all(target_family = "wasm", miden)))]
14use miden_serde_utils::{
15 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
16};
17use thiserror::Error;
18
19pub const WORD_SIZE_FELTS: usize = 4;
20pub const WORD_SIZE_BYTES: usize = 32;
21
22#[cfg(not(all(target_family = "wasm", miden)))]
23use p3_field::integers::QuotientMap;
24
25use super::Felt;
26use crate::utils::bytes_to_hex_string;
27
28mod lexicographic;
29pub use lexicographic::LexicographicWord;
30
31#[cfg(test)]
32mod tests;
33
34#[derive(Default, Copy, Clone, Eq, PartialEq)]
42#[cfg_attr(
43 not(all(target_family = "wasm", miden)),
44 derive(serde::Deserialize, serde::Serialize)
45)]
46#[cfg_attr(
47 not(all(target_family = "wasm", miden)),
48 serde(into = "String", try_from = "&str")
49)]
50#[repr(C)]
51#[cfg_attr(all(target_family = "wasm", miden), repr(align(16)))]
52pub struct Word {
53 pub a: Felt,
55 pub b: Felt,
56 pub c: Felt,
57 pub d: Felt,
58 }
68
69const _: () = {
72 assert!(WORD_SIZE_FELTS == 4, "WORD_SIZE_FELTS is assumed to be 4");
73 assert!(WORD_SIZE_BYTES == 32, "WORD_SIZE_BYTES is assumed to be 32");
74 assert!(core::mem::size_of::<Word>() == WORD_SIZE_FELTS * core::mem::size_of::<Felt>());
75 assert!(core::mem::offset_of!(Word, a) == 0);
76 assert!(core::mem::offset_of!(Word, b) == core::mem::size_of::<Felt>());
77 assert!(core::mem::offset_of!(Word, c) == 2 * core::mem::size_of::<Felt>());
78 assert!(core::mem::offset_of!(Word, d) == 3 * core::mem::size_of::<Felt>());
79};
80
81impl core::fmt::Debug for Word {
82 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83 f.debug_tuple("Word").field(&self.into_elements()).finish()
84 }
85}
86
87impl Word {
88 pub const SERIALIZED_SIZE: usize = WORD_SIZE_BYTES;
90
91 pub const fn new(value: [Felt; WORD_SIZE_FELTS]) -> Self {
93 let [a, b, c, d] = value;
94 Self { a, b, c, d }
95 }
96
97 pub const fn into_elements(self) -> [Felt; WORD_SIZE_FELTS] {
99 [self.a, self.b, self.c, self.d]
100 }
101
102 fn as_elements_array(&self) -> &[Felt; WORD_SIZE_FELTS] {
108 unsafe { &*(&self.a as *const Felt as *const [Felt; WORD_SIZE_FELTS]) }
109 }
110
111 fn as_elements_array_mut(&mut self) -> &mut [Felt; WORD_SIZE_FELTS] {
117 unsafe { &mut *(&mut self.a as *mut Felt as *mut [Felt; WORD_SIZE_FELTS]) }
118 }
119
120 #[cfg(not(all(target_family = "wasm", miden)))]
136 pub const fn parse(hex: &str) -> Result<Self, &'static str> {
137 const fn parse_hex_digit(digit: u8) -> Result<u8, &'static str> {
138 match digit {
139 b'0'..=b'9' => Ok(digit - b'0'),
140 b'A'..=b'F' => Ok(digit - b'A' + 0x0a),
141 b'a'..=b'f' => Ok(digit - b'a' + 0x0a),
142 _ => Err("Invalid hex character"),
143 }
144 }
145 let hex_bytes = match hex.as_bytes() {
147 [b'0', b'x', rest @ ..] => rest,
148 _ => return Err("Hex string must have a \"0x\" prefix"),
149 };
150
151 if hex_bytes.len() > 64 {
152 return Err("Hex string has more than 64 characters");
153 }
154
155 let mut felts = [0u64; 4];
156 let mut i = 0;
157 while i < hex_bytes.len() {
158 let hex_digit = match parse_hex_digit(hex_bytes[i]) {
159 Ok(v) => v as u64,
162 Err(e) => return Err(e),
163 };
164
165 let inibble = if i.is_multiple_of(2) {
168 (i + 1) % 16
169 } else {
170 (i - 1) % 16
171 };
172
173 let value = hex_digit << (inibble * 4);
174 felts[i / 2 / 8] += value;
175
176 i += 1;
177 }
178
179 let mut idx = 0;
182 while idx < felts.len() {
183 if felts[idx] >= Felt::ORDER {
184 return Err("Felt overflow");
185 }
186 idx += 1;
187 }
188
189 Ok(Self::new([
190 Felt::new(felts[0]),
191 Felt::new(felts[1]),
192 Felt::new(felts[2]),
193 Felt::new(felts[3]),
194 ]))
195 }
196
197 pub const fn empty() -> Self {
199 Self::new([Felt::ZERO; WORD_SIZE_FELTS])
200 }
201
202 pub fn is_empty(&self) -> bool {
204 let elements = self.as_elements_array();
205 elements[0] == Felt::ZERO
206 && elements[1] == Felt::ZERO
207 && elements[2] == Felt::ZERO
208 && elements[3] == Felt::ZERO
209 }
210
211 pub fn as_elements(&self) -> &[Felt] {
213 self.as_elements_array()
214 }
215
216 pub fn as_bytes(&self) -> [u8; WORD_SIZE_BYTES] {
218 let mut result = [0; WORD_SIZE_BYTES];
219
220 let elements = self.as_elements_array();
221 result[..8].copy_from_slice(&elements[0].as_canonical_u64().to_le_bytes());
222 result[8..16].copy_from_slice(&elements[1].as_canonical_u64().to_le_bytes());
223 result[16..24].copy_from_slice(&elements[2].as_canonical_u64().to_le_bytes());
224 result[24..].copy_from_slice(&elements[3].as_canonical_u64().to_le_bytes());
225
226 result
227 }
228
229 pub fn words_as_elements_iter<'a, I>(words: I) -> impl Iterator<Item = &'a Felt>
231 where
232 I: Iterator<Item = &'a Self>,
233 {
234 words.flat_map(|d| d.as_elements().iter())
235 }
236
237 pub fn words_as_elements(words: &[Self]) -> &[Felt] {
239 let len = words.len() * WORD_SIZE_FELTS;
240 unsafe { slice::from_raw_parts(words.as_ptr() as *const Felt, len) }
241 }
242
243 pub fn to_hex(&self) -> String {
245 bytes_to_hex_string(self.as_bytes())
246 }
247
248 pub fn to_vec(&self) -> Vec<Felt> {
250 self.as_elements().to_vec()
251 }
252
253 pub fn reversed(&self) -> Self {
255 Word {
256 a: self.d,
257 b: self.c,
258 c: self.b,
259 d: self.a,
260 }
261 }
262}
263
264impl Hash for Word {
265 fn hash<H: Hasher>(&self, state: &mut H) {
266 state.write(&self.as_bytes());
267 }
268}
269
270impl Deref for Word {
271 type Target = [Felt; WORD_SIZE_FELTS];
272
273 fn deref(&self) -> &Self::Target {
274 self.as_elements_array()
275 }
276}
277
278impl DerefMut for Word {
279 fn deref_mut(&mut self) -> &mut Self::Target {
280 self.as_elements_array_mut()
281 }
282}
283
284impl Index<usize> for Word {
285 type Output = Felt;
286
287 fn index(&self, index: usize) -> &Self::Output {
288 &self.as_elements_array()[index]
289 }
290}
291
292impl IndexMut<usize> for Word {
293 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
294 &mut self.as_elements_array_mut()[index]
295 }
296}
297
298impl Index<Range<usize>> for Word {
299 type Output = [Felt];
300
301 fn index(&self, index: Range<usize>) -> &Self::Output {
302 &self.as_elements_array()[index]
303 }
304}
305
306impl IndexMut<Range<usize>> for Word {
307 fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
308 &mut self.as_elements_array_mut()[index]
309 }
310}
311
312impl Ord for Word {
313 fn cmp(&self, other: &Self) -> Ordering {
314 for (felt0, felt1) in self
328 .iter()
329 .rev()
330 .map(Felt::as_canonical_u64)
331 .zip(other.iter().rev().map(Felt::as_canonical_u64))
332 {
333 let ordering = felt0.cmp(&felt1);
334 if let Ordering::Less | Ordering::Greater = ordering {
335 return ordering;
336 }
337 }
338
339 Ordering::Equal
340 }
341}
342
343impl PartialOrd for Word {
344 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
345 Some(self.cmp(other))
346 }
347}
348
349#[cfg(not(all(target_family = "wasm", miden)))]
350impl Display for Word {
351 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
352 write!(f, "{}", self.to_hex())
353 }
354}
355
356#[derive(Debug, Error)]
361pub enum WordError {
362 #[error("hex encoded values of a word are invalid")]
364 HexParse(#[from] crate::utils::HexParseError),
365 #[error("failed to convert to field element: {0}")]
367 InvalidFieldElement(String),
368 #[error("invalid input length: expected {1} {0}, but received {2}")]
370 InvalidInputLength(&'static str, usize, usize),
371 #[error("failed to convert the word's field elements to type {0}")]
373 TypeConversion(&'static str),
374}
375
376impl TryFrom<&Word> for [bool; WORD_SIZE_FELTS] {
377 type Error = WordError;
378
379 fn try_from(value: &Word) -> Result<Self, Self::Error> {
380 (*value).try_into()
381 }
382}
383
384impl TryFrom<Word> for [bool; WORD_SIZE_FELTS] {
385 type Error = WordError;
386
387 fn try_from(value: Word) -> Result<Self, Self::Error> {
388 fn to_bool(v: u64) -> Option<bool> {
389 if v <= 1 { Some(v == 1) } else { None }
390 }
391
392 let [a, b, c, d] = value.into_elements();
393 Ok([
394 to_bool(a.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
395 to_bool(b.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
396 to_bool(c.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
397 to_bool(d.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
398 ])
399 }
400}
401
402impl TryFrom<&Word> for [u8; WORD_SIZE_FELTS] {
403 type Error = WordError;
404
405 fn try_from(value: &Word) -> Result<Self, Self::Error> {
406 (*value).try_into()
407 }
408}
409
410impl TryFrom<Word> for [u8; WORD_SIZE_FELTS] {
411 type Error = WordError;
412
413 fn try_from(value: Word) -> Result<Self, Self::Error> {
414 let [a, b, c, d] = value.into_elements();
415 Ok([
416 a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
417 b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
418 c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
419 d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
420 ])
421 }
422}
423
424impl TryFrom<&Word> for [u16; WORD_SIZE_FELTS] {
425 type Error = WordError;
426
427 fn try_from(value: &Word) -> Result<Self, Self::Error> {
428 (*value).try_into()
429 }
430}
431
432impl TryFrom<Word> for [u16; WORD_SIZE_FELTS] {
433 type Error = WordError;
434
435 fn try_from(value: Word) -> Result<Self, Self::Error> {
436 let [a, b, c, d] = value.into_elements();
437 Ok([
438 a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
439 b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
440 c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
441 d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
442 ])
443 }
444}
445
446impl TryFrom<&Word> for [u32; WORD_SIZE_FELTS] {
447 type Error = WordError;
448
449 fn try_from(value: &Word) -> Result<Self, Self::Error> {
450 (*value).try_into()
451 }
452}
453
454impl TryFrom<Word> for [u32; WORD_SIZE_FELTS] {
455 type Error = WordError;
456
457 fn try_from(value: Word) -> Result<Self, Self::Error> {
458 let [a, b, c, d] = value.into_elements();
459 Ok([
460 a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
461 b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
462 c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
463 d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
464 ])
465 }
466}
467
468impl From<&Word> for [u64; WORD_SIZE_FELTS] {
469 fn from(value: &Word) -> Self {
470 (*value).into()
471 }
472}
473
474impl From<Word> for [u64; WORD_SIZE_FELTS] {
475 fn from(value: Word) -> Self {
476 value.into_elements().map(|felt| felt.as_canonical_u64())
477 }
478}
479
480impl From<&Word> for [Felt; WORD_SIZE_FELTS] {
481 fn from(value: &Word) -> Self {
482 (*value).into()
483 }
484}
485
486impl From<Word> for [Felt; WORD_SIZE_FELTS] {
487 fn from(value: Word) -> Self {
488 value.into_elements()
489 }
490}
491
492impl From<&Word> for [u8; WORD_SIZE_BYTES] {
493 fn from(value: &Word) -> Self {
494 (*value).into()
495 }
496}
497
498impl From<Word> for [u8; WORD_SIZE_BYTES] {
499 fn from(value: Word) -> Self {
500 value.as_bytes()
501 }
502}
503
504#[cfg(not(all(target_family = "wasm", miden)))]
505impl From<&Word> for String {
506 fn from(value: &Word) -> Self {
508 (*value).into()
509 }
510}
511
512#[cfg(not(all(target_family = "wasm", miden)))]
513impl From<Word> for String {
514 fn from(value: Word) -> Self {
516 value.to_hex()
517 }
518}
519
520impl From<&[bool; WORD_SIZE_FELTS]> for Word {
524 fn from(value: &[bool; WORD_SIZE_FELTS]) -> Self {
525 (*value).into()
526 }
527}
528
529impl From<[bool; WORD_SIZE_FELTS]> for Word {
530 fn from(value: [bool; WORD_SIZE_FELTS]) -> Self {
531 [value[0] as u32, value[1] as u32, value[2] as u32, value[3] as u32].into()
532 }
533}
534
535impl From<&[u8; WORD_SIZE_FELTS]> for Word {
536 fn from(value: &[u8; WORD_SIZE_FELTS]) -> Self {
537 (*value).into()
538 }
539}
540
541impl From<[u8; WORD_SIZE_FELTS]> for Word {
542 fn from(value: [u8; WORD_SIZE_FELTS]) -> Self {
543 Self::new([
544 Felt::from_u8(value[0]),
545 Felt::from_u8(value[1]),
546 Felt::from_u8(value[2]),
547 Felt::from_u8(value[3]),
548 ])
549 }
550}
551
552impl From<&[u16; WORD_SIZE_FELTS]> for Word {
553 fn from(value: &[u16; WORD_SIZE_FELTS]) -> Self {
554 (*value).into()
555 }
556}
557
558impl From<[u16; WORD_SIZE_FELTS]> for Word {
559 fn from(value: [u16; WORD_SIZE_FELTS]) -> Self {
560 Self::new([
561 Felt::from_u16(value[0]),
562 Felt::from_u16(value[1]),
563 Felt::from_u16(value[2]),
564 Felt::from_u16(value[3]),
565 ])
566 }
567}
568
569impl From<&[u32; WORD_SIZE_FELTS]> for Word {
570 fn from(value: &[u32; WORD_SIZE_FELTS]) -> Self {
571 (*value).into()
572 }
573}
574
575impl From<[u32; WORD_SIZE_FELTS]> for Word {
576 fn from(value: [u32; WORD_SIZE_FELTS]) -> Self {
577 Self::new([
578 Felt::from_u32(value[0]),
579 Felt::from_u32(value[1]),
580 Felt::from_u32(value[2]),
581 Felt::from_u32(value[3]),
582 ])
583 }
584}
585
586impl TryFrom<&[u64; WORD_SIZE_FELTS]> for Word {
587 type Error = WordError;
588
589 fn try_from(value: &[u64; WORD_SIZE_FELTS]) -> Result<Self, WordError> {
590 (*value).try_into()
591 }
592}
593
594impl TryFrom<[u64; WORD_SIZE_FELTS]> for Word {
595 type Error = WordError;
596
597 fn try_from(value: [u64; WORD_SIZE_FELTS]) -> Result<Self, WordError> {
598 let err = || WordError::InvalidFieldElement("value >= field modulus".into());
599 Ok(Self::new([
600 Felt::from_canonical_checked(value[0]).ok_or_else(err)?,
601 Felt::from_canonical_checked(value[1]).ok_or_else(err)?,
602 Felt::from_canonical_checked(value[2]).ok_or_else(err)?,
603 Felt::from_canonical_checked(value[3]).ok_or_else(err)?,
604 ]))
605 }
606}
607
608impl From<&[Felt; WORD_SIZE_FELTS]> for Word {
609 fn from(value: &[Felt; WORD_SIZE_FELTS]) -> Self {
610 Self::new(*value)
611 }
612}
613
614impl From<[Felt; WORD_SIZE_FELTS]> for Word {
615 fn from(value: [Felt; WORD_SIZE_FELTS]) -> Self {
616 Self::new(value)
617 }
618}
619
620impl TryFrom<&[u8; WORD_SIZE_BYTES]> for Word {
621 type Error = WordError;
622
623 fn try_from(value: &[u8; WORD_SIZE_BYTES]) -> Result<Self, Self::Error> {
624 (*value).try_into()
625 }
626}
627
628impl TryFrom<[u8; WORD_SIZE_BYTES]> for Word {
629 type Error = WordError;
630
631 fn try_from(value: [u8; WORD_SIZE_BYTES]) -> Result<Self, Self::Error> {
632 let a = u64::from_le_bytes(value[0..8].try_into().unwrap());
635 let b = u64::from_le_bytes(value[8..16].try_into().unwrap());
636 let c = u64::from_le_bytes(value[16..24].try_into().unwrap());
637 let d = u64::from_le_bytes(value[24..32].try_into().unwrap());
638
639 let err = || WordError::InvalidFieldElement("value >= field modulus".into());
640 let a: Felt = Felt::from_canonical_checked(a).ok_or_else(err)?;
641 let b: Felt = Felt::from_canonical_checked(b).ok_or_else(err)?;
642 let c: Felt = Felt::from_canonical_checked(c).ok_or_else(err)?;
643 let d: Felt = Felt::from_canonical_checked(d).ok_or_else(err)?;
644
645 Ok(Self::new([a, b, c, d]))
646 }
647}
648
649impl TryFrom<&[u8]> for Word {
650 type Error = WordError;
651
652 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
653 let value: [u8; WORD_SIZE_BYTES] = value
654 .try_into()
655 .map_err(|_| WordError::InvalidInputLength("bytes", WORD_SIZE_BYTES, value.len()))?;
656 value.try_into()
657 }
658}
659
660impl TryFrom<&[Felt]> for Word {
661 type Error = WordError;
662
663 fn try_from(value: &[Felt]) -> Result<Self, Self::Error> {
664 let value: [Felt; WORD_SIZE_FELTS] = value
665 .try_into()
666 .map_err(|_| WordError::InvalidInputLength("elements", WORD_SIZE_FELTS, value.len()))?;
667 Ok(value.into())
668 }
669}
670
671#[cfg(not(all(target_family = "wasm", miden)))]
672impl TryFrom<&str> for Word {
673 type Error = WordError;
674
675 fn try_from(value: &str) -> Result<Self, Self::Error> {
677 crate::utils::hex_to_bytes::<WORD_SIZE_BYTES>(value)
678 .map_err(WordError::HexParse)
679 .and_then(Word::try_from)
680 }
681}
682
683#[cfg(not(all(target_family = "wasm", miden)))]
684impl TryFrom<String> for Word {
685 type Error = WordError;
686
687 fn try_from(value: String) -> Result<Self, Self::Error> {
689 value.as_str().try_into()
690 }
691}
692
693#[cfg(not(all(target_family = "wasm", miden)))]
694impl TryFrom<&String> for Word {
695 type Error = WordError;
696
697 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)))]
707impl Serializable for Word {
708 fn write_into<W: ByteWriter>(&self, target: &mut W) {
709 target.write_bytes(&self.as_bytes());
710 }
711
712 fn get_size_hint(&self) -> usize {
713 Self::SERIALIZED_SIZE
714 }
715}
716
717#[cfg(not(all(target_family = "wasm", miden)))]
718impl Deserializable for Word {
719 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
720 let mut inner: [Felt; WORD_SIZE_FELTS] = [Felt::ZERO; WORD_SIZE_FELTS];
721 for inner in inner.iter_mut() {
722 let e = source.read_u64()?;
723 if e >= Felt::ORDER {
724 return Err(DeserializationError::InvalidValue(String::from(
725 "value not in the appropriate range",
726 )));
727 }
728 *inner = Felt::new(e);
729 }
730
731 Ok(Self::new(inner))
732 }
733
734 fn min_serialized_size() -> usize {
735 Self::SERIALIZED_SIZE
736 }
737}
738
739impl IntoIterator for Word {
742 type Item = Felt;
743 type IntoIter = <[Felt; 4] as IntoIterator>::IntoIter;
744
745 fn into_iter(self) -> Self::IntoIter {
746 self.into_elements().into_iter()
747 }
748}
749
750#[cfg(not(all(target_family = "wasm", miden)))]
757#[macro_export]
758macro_rules! word {
759 ($hex:expr) => {{
760 let word: Word = match $crate::word::Word::parse($hex) {
761 Ok(v) => v,
762 Err(e) => panic!("{}", e),
763 };
764
765 word
766 }};
767}
768
769#[cfg(all(any(test, feature = "testing"), not(all(target_family = "wasm", miden))))]
773mod arbitrary {
774 use proptest::prelude::*;
775
776 use super::{Felt, Word};
777
778 impl Arbitrary for Word {
779 type Parameters = ();
780 type Strategy = BoxedStrategy<Self>;
781
782 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
783 prop::array::uniform4(any::<Felt>()).prop_map(Word::new).no_shrink().boxed()
784 }
785 }
786}