Skip to content

Commit

Permalink
feat: impl a few num-traits on Felt, behind a feature flag
Browse files Browse the repository at this point in the history
  • Loading branch information
tdelabro committed Nov 28, 2023
1 parent 02e19f3 commit f35dce3
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 76 deletions.
23 changes: 17 additions & 6 deletions crates/starknet-types-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,34 @@ readme = "README.md"

[dependencies]
bitvec = { version = "1.0.1", default-features = false }
serde = { version = "1.0.163", optional = true, default-features = false }
lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "f940e14ed17370d29fe129951448037d11b65ce8", default-features = false}
lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "f940e14ed17370d29fe129951448037d11b65ce8", default-features = false, optional = true}


arbitrary = { version = "1.3.0", optional = true, default-features = false }
num-traits = { version = "0.2.16", default-features = false }
num-bigint = {version = "0.4.4", default-features = false}
num-integer = {version = "0.1.45", default-features = false}

# Optional
arbitrary = { version = "1.3.0", optional = true }
serde = { version = "1.0.163", optional = true, default-features = false }
lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "f940e14ed17370d29fe129951448037d11b65ce8", default-features = false, optional = true}

[features]
default = ["std", "serde", "curve"]
default = ["std", "serde", "curve", "num-traits"]
std = [
"bitvec/std",
"lambdaworks-math/std",
"num-traits/std",
"num-bigint/std",
"num-integer/std",
"serde?/std",
]
curve = []
hash = ["dep:lambdaworks-crypto"]
std = []
alloc = ["serde?/alloc"]
num-traits = []
arbitrary = ["std", "dep:arbitrary"]
serde = ["dep:serde"]
hash = ["dep:lambdaworks-crypto"]

[dev-dependencies]
proptest = "1.1.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use lambdaworks_math::{field::element::FieldElement, unsigned_integer::element::UnsignedInteger};
use num_traits::Zero;
use proptest::prelude::*;

use crate::felt::Felt;
Expand Down Expand Up @@ -37,7 +36,7 @@ fn any_felt() -> impl Strategy<Value = Felt> {
/// Returns a [`Strategy`] that generates any nonzero Felt
/// This is used to generate input values for proptests
pub fn nonzero_felt() -> impl Strategy<Value = Felt> {
any_felt().prop_filter("is zero", |x| !x.is_zero())
any_felt().prop_filter("is zero", |&x| x != Felt::ZERO)
}

impl Arbitrary for Felt {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
#[cfg(test)]
mod felt_arbitrary;

use core::ops::{Add, Mul, Neg};

use bitvec::array::BitArray;
use num_bigint::BigInt;

use num_traits::{One, Zero};

#[cfg(feature = "num-traits")]
mod num_traits_impl;

use num_integer::Integer;
use num_traits::{FromPrimitive, One, ToPrimitive, Zero};

#[cfg(target_pointer_width = "64")]
pub type BitArrayStore = [u64; 4];
Expand Down Expand Up @@ -396,7 +404,7 @@ impl TryFrom<Felt> for NonZeroFelt {
type Error = FeltIsZeroError;

fn try_from(value: Felt) -> Result<Self, Self::Error> {
if value.is_zero() {
if value == Felt::ZERO {
Err(FeltIsZeroError)
} else {
Ok(Self(value.0))
Expand All @@ -408,7 +416,7 @@ impl TryFrom<&Felt> for NonZeroFelt {
type Error = FeltIsZeroError;

fn try_from(value: &Felt) -> Result<Self, Self::Error> {
if value.is_zero() {
if *value == Felt::ZERO {
Err(FeltIsZeroError)
} else {
Ok(Self(value.0))
Expand Down Expand Up @@ -455,57 +463,6 @@ impl_from!(i32, i128);
impl_from!(i64, i128);
impl_from!(isize, i128);

impl FromPrimitive for Felt {
fn from_i64(value: i64) -> Option<Self> {
Some(value.into())
}

fn from_u64(value: u64) -> Option<Self> {
Some(value.into())
}

fn from_i128(value: i128) -> Option<Self> {
Some(value.into())
}

fn from_u128(value: u128) -> Option<Self> {
Some(value.into())
}
}

// TODO: we need to decide whether we want conversions to signed primitives
// will support converting the high end of the field to negative.
impl ToPrimitive for Felt {
fn to_u64(&self) -> Option<u64> {
self.to_u128().and_then(|x| u64::try_from(x).ok())
}

fn to_i64(&self) -> Option<i64> {
self.to_u128().and_then(|x| i64::try_from(x).ok())
}

fn to_u128(&self) -> Option<u128> {
match self.0.representative().limbs {
[0, 0, hi, lo] => Some((lo as u128) | ((hi as u128) << 64)),
_ => None,
}
}

fn to_i128(&self) -> Option<i128> {
self.to_u128().and_then(|x| i128::try_from(x).ok())
}
}

impl Zero for Felt {
fn is_zero(&self) -> bool {
*self == Felt::ZERO
}

fn zero() -> Felt {
Felt::ZERO
}
}

impl Add<&Felt> for u64 {
type Output = Option<u64>;

Expand Down Expand Up @@ -863,7 +820,7 @@ mod formatting {
/// Represents [Felt] in decimal by default.
impl fmt::Display for Felt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_zero() {
if *self == Felt::ZERO {
return write!(f, "0");
}

Expand Down Expand Up @@ -1172,7 +1129,7 @@ mod test {

#[test]
fn non_zero_is_not_zero(x in nonzero_felt()) {
prop_assert!(!x.is_zero())
prop_assert!(x != Felt::ZERO)
}

#[test]
Expand Down Expand Up @@ -1253,21 +1210,11 @@ mod test {
assert_eq!(Felt::MAX.to_bytes_be(), max_bytes);
}

#[test]
fn zero_is_zero() {
assert!(Felt::ZERO.is_zero());
}

#[test]
fn non_zero_felt_from_zero_should_fail() {
assert!(NonZeroFelt::try_from(Felt::ZERO).is_err());
}

#[test]
fn default_is_zero() {
assert!(Felt::default().is_zero());
}

#[test]
fn mul_operations() {
assert_eq!(Felt::ONE * Felt::THREE, Felt::THREE);
Expand Down
123 changes: 123 additions & 0 deletions crates/starknet-types-core/src/felt/num_traits_impl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use super::Felt;
use num_traits::{FromPrimitive, Inv, One, Pow, ToPrimitive, Zero};

impl FromPrimitive for Felt {
fn from_i64(value: i64) -> Option<Self> {
Some(value.into())
}

fn from_u64(value: u64) -> Option<Self> {
Some(value.into())
}

fn from_i128(value: i128) -> Option<Self> {
Some(value.into())
}

fn from_u128(value: u128) -> Option<Self> {
Some(value.into())
}
}

// TODO: we need to decide whether we want conversions to signed primitives
// will support converting the high end of the field to negative.
impl ToPrimitive for Felt {
fn to_u64(&self) -> Option<u64> {
self.to_u128().and_then(|x| u64::try_from(x).ok())
}

fn to_i64(&self) -> Option<i64> {
self.to_u128().and_then(|x| i64::try_from(x).ok())
}

fn to_u128(&self) -> Option<u128> {
match self.0.representative().limbs {
[0, 0, hi, lo] => Some((lo as u128) | ((hi as u128) << 64)),
_ => None,
}
}

fn to_i128(&self) -> Option<i128> {
self.to_u128().and_then(|x| i128::try_from(x).ok())
}
}

impl Zero for Felt {
fn is_zero(&self) -> bool {
*self == Felt::ZERO
}

fn zero() -> Felt {
Felt::ZERO
}
}

impl One for Felt {
fn one() -> Self {
Felt::ONE
}
}

impl Inv for Felt {
type Output = Option<Self>;

fn inv(self) -> Self::Output {
self.inverse()
}
}

impl Pow<u8> for Felt {
type Output = Self;

fn pow(self, rhs: u8) -> Self::Output {
Self(self.0.pow(rhs as u128))
}
}
impl Pow<u16> for Felt {
type Output = Self;

fn pow(self, rhs: u16) -> Self::Output {
Self(self.0.pow(rhs as u128))
}
}
impl Pow<u32> for Felt {
type Output = Self;

fn pow(self, rhs: u32) -> Self::Output {
Self(self.0.pow(rhs as u128))
}
}
impl Pow<u64> for Felt {
type Output = Self;

fn pow(self, rhs: u64) -> Self::Output {
Self(self.0.pow(rhs as u128))
}
}
impl Pow<u128> for Felt {
type Output = Self;

fn pow(self, rhs: u128) -> Self::Output {
Self(self.0.pow(rhs))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn zero_is_zero() {
assert!(Felt::ZERO.is_zero());
}

#[test]
fn one_is_one() {
assert!(Felt::ONE.is_one());
}

#[test]
fn default_is_zero() {
assert!(Felt::default().is_zero());
}
}
2 changes: 0 additions & 2 deletions crates/starknet-types-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,3 @@ pub mod curve;
pub mod hash;

pub mod felt;
#[cfg(test)]
mod felt_arbitrary;

0 comments on commit f35dce3

Please sign in to comment.