use alloc::{string::String, vec::Vec};
#[cfg(not(all(target_family = "wasm", miden)))]
use core::fmt::Display;
use core::{
cmp::Ordering,
hash::{Hash, Hasher},
mem::size_of,
ops::{Deref, DerefMut, Index, IndexMut, Range},
slice,
};
#[cfg(not(all(target_family = "wasm", miden)))]
use miden_serde_utils::{
ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
};
#[cfg(not(all(target_family = "wasm", miden)))]
use p3_field::integers::QuotientMap;
use thiserror::Error;
use super::Felt;
use crate::utils::bytes_to_hex_string;
#[cfg(test)]
mod tests;
#[derive(Default, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(
not(all(target_family = "wasm", miden)),
derive(serde::Deserialize, serde::Serialize)
)]
#[cfg_attr(
not(all(target_family = "wasm", miden)),
serde(into = "String", try_from = "&str")
)]
#[repr(C)]
#[cfg_attr(all(target_family = "wasm", miden), repr(align(16)))]
pub struct Word {
pub a: Felt,
pub b: Felt,
pub c: Felt,
pub d: Felt,
}
const _: () = {
assert!(Word::NUM_ELEMENTS == 4, "Word::NUM_ELEMENTS is assumed to be 4");
assert!(Word::SERIALIZED_SIZE == 32, "Word::SERIALIZED_SIZE is assumed to be 32");
assert!(size_of::<Word>() == Word::NUM_ELEMENTS * size_of::<Felt>());
assert!(core::mem::offset_of!(Word, a) == 0);
assert!(core::mem::offset_of!(Word, b) == size_of::<Felt>());
assert!(core::mem::offset_of!(Word, c) == 2 * size_of::<Felt>());
assert!(core::mem::offset_of!(Word, d) == 3 * size_of::<Felt>());
};
impl core::fmt::Debug for Word {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Word").field(&self.into_elements()).finish()
}
}
impl Word {
pub const NUM_ELEMENTS: usize = 4;
pub const SERIALIZED_SIZE: usize = 32;
pub const fn new(value: [Felt; Self::NUM_ELEMENTS]) -> Self {
let [a, b, c, d] = value;
Self { a, b, c, d }
}
pub const fn into_elements(self) -> [Felt; Self::NUM_ELEMENTS] {
[self.a, self.b, self.c, self.d]
}
fn as_elements_array(&self) -> &[Felt; Self::NUM_ELEMENTS] {
unsafe { &*(&self.a as *const Felt as *const [Felt; Self::NUM_ELEMENTS]) }
}
fn as_elements_array_mut(&mut self) -> &mut [Felt; Self::NUM_ELEMENTS] {
unsafe { &mut *(&mut self.a as *mut Felt as *mut [Felt; Self::NUM_ELEMENTS]) }
}
#[cfg(not(all(target_family = "wasm", miden)))]
pub const fn parse(hex: &str) -> Result<Self, &'static str> {
const fn parse_hex_digit(digit: u8) -> Result<u8, &'static str> {
match digit {
b'0'..=b'9' => Ok(digit - b'0'),
b'A'..=b'F' => Ok(digit - b'A' + 0x0a),
b'a'..=b'f' => Ok(digit - b'a' + 0x0a),
_ => Err("Invalid hex character"),
}
}
let hex_bytes = match hex.as_bytes() {
[b'0', b'x', rest @ ..] => rest,
_ => return Err("Hex string must have a \"0x\" prefix"),
};
if hex_bytes.len() > 64 {
return Err("Hex string has more than 64 characters");
}
let mut felts = [0u64; 4];
let mut i = 0;
while i < hex_bytes.len() {
let hex_digit = match parse_hex_digit(hex_bytes[i]) {
Ok(v) => v as u64,
Err(e) => return Err(e),
};
let inibble = if i.is_multiple_of(2) {
(i + 1) % 16
} else {
(i - 1) % 16
};
let value = hex_digit << (inibble * 4);
felts[i / 2 / 8] += value;
i += 1;
}
let mut idx = 0;
while idx < felts.len() {
if felts[idx] >= Felt::ORDER {
return Err("Felt overflow");
}
idx += 1;
}
Ok(Self::new([
Felt::new_unchecked(felts[0]),
Felt::new_unchecked(felts[1]),
Felt::new_unchecked(felts[2]),
Felt::new_unchecked(felts[3]),
]))
}
pub const fn empty() -> Self {
Self::new([Felt::ZERO; Self::NUM_ELEMENTS])
}
pub fn is_empty(&self) -> bool {
let elements = self.as_elements_array();
elements[0] == Felt::ZERO
&& elements[1] == Felt::ZERO
&& elements[2] == Felt::ZERO
&& elements[3] == Felt::ZERO
}
pub fn as_elements(&self) -> &[Felt] {
self.as_elements_array()
}
pub fn as_bytes(&self) -> [u8; Self::SERIALIZED_SIZE] {
let mut result = [0; Self::SERIALIZED_SIZE];
let elements = self.as_elements_array();
result[..8].copy_from_slice(&elements[0].as_canonical_u64().to_le_bytes());
result[8..16].copy_from_slice(&elements[1].as_canonical_u64().to_le_bytes());
result[16..24].copy_from_slice(&elements[2].as_canonical_u64().to_le_bytes());
result[24..].copy_from_slice(&elements[3].as_canonical_u64().to_le_bytes());
result
}
pub fn words_as_elements_iter<'a, I>(words: I) -> impl Iterator<Item = &'a Felt>
where
I: Iterator<Item = &'a Self>,
{
words.flat_map(|d| d.as_elements().iter())
}
pub fn words_as_elements(words: &[Self]) -> &[Felt] {
let len = words.len() * Self::NUM_ELEMENTS;
unsafe { slice::from_raw_parts(words.as_ptr() as *const Felt, len) }
}
pub fn to_hex(&self) -> String {
bytes_to_hex_string(self.as_bytes())
}
pub fn to_vec(&self) -> Vec<Felt> {
self.as_elements().to_vec()
}
pub fn reversed(&self) -> Self {
Word {
a: self.d,
b: self.c,
c: self.b,
d: self.a,
}
}
}
impl Hash for Word {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.as_bytes());
}
}
impl Deref for Word {
type Target = [Felt; Word::NUM_ELEMENTS];
fn deref(&self) -> &Self::Target {
self.as_elements_array()
}
}
impl DerefMut for Word {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_elements_array_mut()
}
}
impl Index<usize> for Word {
type Output = Felt;
fn index(&self, index: usize) -> &Self::Output {
&self.as_elements_array()[index]
}
}
impl IndexMut<usize> for Word {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.as_elements_array_mut()[index]
}
}
impl Index<Range<usize>> for Word {
type Output = [Felt];
fn index(&self, index: Range<usize>) -> &Self::Output {
&self.as_elements_array()[index]
}
}
impl IndexMut<Range<usize>> for Word {
fn index_mut(&mut self, index: Range<usize>) -> &mut Self::Output {
&mut self.as_elements_array_mut()[index]
}
}
impl Ord for Word {
fn cmp(&self, other: &Self) -> Ordering {
for (felt0, felt1) in self
.iter()
.rev()
.map(Felt::as_canonical_u64)
.zip(other.iter().rev().map(Felt::as_canonical_u64))
{
let ordering = felt0.cmp(&felt1);
if let Ordering::Less | Ordering::Greater = ordering {
return ordering;
}
}
Ordering::Equal
}
}
impl PartialOrd for Word {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
impl Display for Word {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.to_hex())
}
}
#[derive(Debug, Error)]
pub enum WordError {
#[error("hex encoded values of a word are invalid")]
HexParse(#[from] crate::utils::HexParseError),
#[error("failed to convert to field element: {0}")]
InvalidFieldElement(String),
#[error("invalid input length: expected {1} {0}, but received {2}")]
InvalidInputLength(&'static str, usize, usize),
#[error("failed to convert the word's field elements to type {0}")]
TypeConversion(&'static str),
}
impl TryFrom<&Word> for [bool; Word::NUM_ELEMENTS] {
type Error = WordError;
fn try_from(value: &Word) -> Result<Self, Self::Error> {
(*value).try_into()
}
}
impl TryFrom<Word> for [bool; Word::NUM_ELEMENTS] {
type Error = WordError;
fn try_from(value: Word) -> Result<Self, Self::Error> {
fn to_bool(v: u64) -> Option<bool> {
if v <= 1 { Some(v == 1) } else { None }
}
let [a, b, c, d] = value.into_elements();
Ok([
to_bool(a.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
to_bool(b.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
to_bool(c.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
to_bool(d.as_canonical_u64()).ok_or(WordError::TypeConversion("bool"))?,
])
}
}
impl TryFrom<&Word> for [u8; Word::NUM_ELEMENTS] {
type Error = WordError;
fn try_from(value: &Word) -> Result<Self, Self::Error> {
(*value).try_into()
}
}
impl TryFrom<Word> for [u8; Word::NUM_ELEMENTS] {
type Error = WordError;
fn try_from(value: Word) -> Result<Self, Self::Error> {
let [a, b, c, d] = value.into_elements();
Ok([
a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u8"))?,
])
}
}
impl TryFrom<&Word> for [u16; Word::NUM_ELEMENTS] {
type Error = WordError;
fn try_from(value: &Word) -> Result<Self, Self::Error> {
(*value).try_into()
}
}
impl TryFrom<Word> for [u16; Word::NUM_ELEMENTS] {
type Error = WordError;
fn try_from(value: Word) -> Result<Self, Self::Error> {
let [a, b, c, d] = value.into_elements();
Ok([
a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u16"))?,
])
}
}
impl TryFrom<&Word> for [u32; Word::NUM_ELEMENTS] {
type Error = WordError;
fn try_from(value: &Word) -> Result<Self, Self::Error> {
(*value).try_into()
}
}
impl TryFrom<Word> for [u32; Word::NUM_ELEMENTS] {
type Error = WordError;
fn try_from(value: Word) -> Result<Self, Self::Error> {
let [a, b, c, d] = value.into_elements();
Ok([
a.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
b.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
c.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
d.as_canonical_u64().try_into().map_err(|_| WordError::TypeConversion("u32"))?,
])
}
}
impl From<&Word> for [u64; Word::NUM_ELEMENTS] {
fn from(value: &Word) -> Self {
(*value).into()
}
}
impl From<Word> for [u64; Word::NUM_ELEMENTS] {
fn from(value: Word) -> Self {
value.into_elements().map(|felt| felt.as_canonical_u64())
}
}
impl From<&Word> for [Felt; Word::NUM_ELEMENTS] {
fn from(value: &Word) -> Self {
(*value).into()
}
}
impl From<Word> for [Felt; Word::NUM_ELEMENTS] {
fn from(value: Word) -> Self {
value.into_elements()
}
}
impl From<&Word> for [u8; Word::SERIALIZED_SIZE] {
fn from(value: &Word) -> Self {
(*value).into()
}
}
impl From<Word> for [u8; Word::SERIALIZED_SIZE] {
fn from(value: Word) -> Self {
value.as_bytes()
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
impl From<&Word> for String {
fn from(value: &Word) -> Self {
(*value).into()
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
impl From<Word> for String {
fn from(value: Word) -> Self {
value.to_hex()
}
}
impl From<&[bool; Word::NUM_ELEMENTS]> for Word {
fn from(value: &[bool; Word::NUM_ELEMENTS]) -> Self {
(*value).into()
}
}
impl From<[bool; Word::NUM_ELEMENTS]> for Word {
fn from(value: [bool; Word::NUM_ELEMENTS]) -> Self {
[value[0] as u32, value[1] as u32, value[2] as u32, value[3] as u32].into()
}
}
impl From<&[u8; Word::NUM_ELEMENTS]> for Word {
fn from(value: &[u8; Word::NUM_ELEMENTS]) -> Self {
(*value).into()
}
}
impl From<[u8; Word::NUM_ELEMENTS]> for Word {
fn from(value: [u8; Word::NUM_ELEMENTS]) -> Self {
Self::new([
Felt::from_u8(value[0]),
Felt::from_u8(value[1]),
Felt::from_u8(value[2]),
Felt::from_u8(value[3]),
])
}
}
impl From<&[u16; Word::NUM_ELEMENTS]> for Word {
fn from(value: &[u16; Word::NUM_ELEMENTS]) -> Self {
(*value).into()
}
}
impl From<[u16; Word::NUM_ELEMENTS]> for Word {
fn from(value: [u16; Word::NUM_ELEMENTS]) -> Self {
Self::new([
Felt::from_u16(value[0]),
Felt::from_u16(value[1]),
Felt::from_u16(value[2]),
Felt::from_u16(value[3]),
])
}
}
impl From<&[u32; Word::NUM_ELEMENTS]> for Word {
fn from(value: &[u32; Word::NUM_ELEMENTS]) -> Self {
(*value).into()
}
}
impl From<[u32; Word::NUM_ELEMENTS]> for Word {
fn from(value: [u32; Word::NUM_ELEMENTS]) -> Self {
Self::new([
Felt::from_u32(value[0]),
Felt::from_u32(value[1]),
Felt::from_u32(value[2]),
Felt::from_u32(value[3]),
])
}
}
impl TryFrom<&[u64; Word::NUM_ELEMENTS]> for Word {
type Error = WordError;
fn try_from(value: &[u64; Word::NUM_ELEMENTS]) -> Result<Self, WordError> {
(*value).try_into()
}
}
impl TryFrom<[u64; Word::NUM_ELEMENTS]> for Word {
type Error = WordError;
fn try_from(value: [u64; Word::NUM_ELEMENTS]) -> Result<Self, WordError> {
let err = || WordError::InvalidFieldElement("value >= field modulus".into());
Ok(Self::new([
Felt::from_canonical_checked(value[0]).ok_or_else(err)?,
Felt::from_canonical_checked(value[1]).ok_or_else(err)?,
Felt::from_canonical_checked(value[2]).ok_or_else(err)?,
Felt::from_canonical_checked(value[3]).ok_or_else(err)?,
]))
}
}
impl From<&[Felt; Word::NUM_ELEMENTS]> for Word {
fn from(value: &[Felt; Word::NUM_ELEMENTS]) -> Self {
Self::new(*value)
}
}
impl From<[Felt; Word::NUM_ELEMENTS]> for Word {
fn from(value: [Felt; Word::NUM_ELEMENTS]) -> Self {
Self::new(value)
}
}
impl TryFrom<&[u8; Word::SERIALIZED_SIZE]> for Word {
type Error = WordError;
fn try_from(value: &[u8; Word::SERIALIZED_SIZE]) -> Result<Self, Self::Error> {
(*value).try_into()
}
}
impl TryFrom<[u8; Word::SERIALIZED_SIZE]> for Word {
type Error = WordError;
fn try_from(value: [u8; Word::SERIALIZED_SIZE]) -> Result<Self, Self::Error> {
let a = u64::from_le_bytes(value[0..8].try_into().unwrap());
let b = u64::from_le_bytes(value[8..16].try_into().unwrap());
let c = u64::from_le_bytes(value[16..24].try_into().unwrap());
let d = u64::from_le_bytes(value[24..32].try_into().unwrap());
let err = || WordError::InvalidFieldElement("value >= field modulus".into());
let a: Felt = Felt::from_canonical_checked(a).ok_or_else(err)?;
let b: Felt = Felt::from_canonical_checked(b).ok_or_else(err)?;
let c: Felt = Felt::from_canonical_checked(c).ok_or_else(err)?;
let d: Felt = Felt::from_canonical_checked(d).ok_or_else(err)?;
Ok(Self::new([a, b, c, d]))
}
}
impl TryFrom<&[u8]> for Word {
type Error = WordError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let value: [u8; Word::SERIALIZED_SIZE] = value.try_into().map_err(|_| {
WordError::InvalidInputLength("bytes", Word::SERIALIZED_SIZE, value.len())
})?;
value.try_into()
}
}
impl TryFrom<&[Felt]> for Word {
type Error = WordError;
fn try_from(value: &[Felt]) -> Result<Self, Self::Error> {
let value: [Felt; Word::NUM_ELEMENTS] = value.try_into().map_err(|_| {
WordError::InvalidInputLength("elements", Word::NUM_ELEMENTS, value.len())
})?;
Ok(value.into())
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
impl TryFrom<&str> for Word {
type Error = WordError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
crate::utils::hex_to_bytes::<{ Word::SERIALIZED_SIZE }>(value)
.map_err(WordError::HexParse)
.and_then(Word::try_from)
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
impl TryFrom<String> for Word {
type Error = WordError;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.as_str().try_into()
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
impl TryFrom<&String> for Word {
type Error = WordError;
fn try_from(value: &String) -> Result<Self, Self::Error> {
value.as_str().try_into()
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
impl Serializable for Word {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_bytes(&self.as_bytes());
}
fn get_size_hint(&self) -> usize {
Self::SERIALIZED_SIZE
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
impl Deserializable for Word {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let mut inner: [Felt; Word::NUM_ELEMENTS] = [Felt::ZERO; Word::NUM_ELEMENTS];
for inner in inner.iter_mut() {
let e = source.read_u64()?;
if e >= Felt::ORDER {
return Err(DeserializationError::InvalidValue(String::from(
"value not in the appropriate range",
)));
}
*inner = Felt::new_unchecked(e);
}
Ok(Self::new(inner))
}
fn min_serialized_size() -> usize {
Self::SERIALIZED_SIZE
}
}
impl IntoIterator for Word {
type Item = Felt;
type IntoIter = <[Felt; 4] as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.into_elements().into_iter()
}
}
#[cfg(not(all(target_family = "wasm", miden)))]
#[macro_export]
macro_rules! word {
($hex:expr) => {{
let word: Word = match $crate::word::Word::parse($hex) {
Ok(v) => v,
Err(e) => panic!("{}", e),
};
word
}};
}
#[cfg(all(any(test, feature = "testing"), not(all(target_family = "wasm", miden))))]
mod arbitrary {
use proptest::prelude::*;
use super::{Felt, Word};
impl Arbitrary for Word {
type Parameters = ();
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
prop::array::uniform4(any::<Felt>()).prop_map(Word::new).no_shrink().boxed()
}
}
}