Skip to content

Commit

Permalink
Impl num traits (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
tdelabro authored Dec 26, 2023
1 parent 05c7565 commit c7cf22e
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 92 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ Cargo.lock
# Added by cargo

/target

*.DS_Store
29 changes: 20 additions & 9 deletions crates/starknet-types-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,38 @@ readme = "README.md"

[dependencies]
bitvec = { version = "1.0.1", default-features = false }
serde = { version = "1.0.163", optional = true, default-features = false }
lambdaworks-math = {version = "0.3.0", default-features = false}
lambdaworks-crypto = {version = "0.3.0", default-features = false, optional = true}
parity-scale-codec = { version = "3.2.2", 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}
num-bigint = { version = "0.4.4", default-features = false }
num-integer = { version = "0.1.45", default-features = false }
lazy_static = { version = "1.4.0", default-features = false, features = [
"spin_no_std",
] }

# Optional
arbitrary = { version = "1.3.0", optional = true }
serde = { version = "1.0.163", optional = true, default-features = false, features = ["alloc"] }
lambdaworks-crypto = { version = "0.3.0", default-features = false, optional = true }
parity-scale-codec = { version = "3.2.2", 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",
]
alloc = []
curve = []
hash = ["dep:lambdaworks-crypto"]
std = ["alloc"]
alloc = ["serde?/alloc"]
arbitrary = ["std", "dep:arbitrary"]
parity-scale-codec = ["dep:parity-scale-codec"]
serde = ["alloc", "dep:serde"]
num-traits = []

[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,11 +1,17 @@
#[cfg(test)]
mod felt_arbitrary;

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

use bitvec::array::BitArray;
use lazy_static::lazy_static;
use num_bigint::{BigInt, BigUint, Sign};
use num_integer::Integer;
use num_traits::Num;
use num_traits::{FromPrimitive, One, ToPrimitive, Zero};
use num_traits::{One, Zero};

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

lazy_static! {
pub static ref CAIRO_PRIME_BIGINT: BigInt = BigInt::from_str_radix(
Expand All @@ -21,12 +27,9 @@ pub type BitArrayStore = [u64; 4];
#[cfg(not(target_pointer_width = "64"))]
pub type BitArrayStore = [u32; 8];

#[cfg(any(test, feature = "alloc"))]
pub extern crate alloc;

use alloc::string::ToString;
#[cfg(not(target_pointer_width = "64"))]
use alloc::vec::Vec;

use lambdaworks_math::{
field::{
element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField,
Expand Down Expand Up @@ -194,16 +197,26 @@ impl Felt {

/// Converts to big-endian bit representation.
/// This is as performant as [to_bits_le](Felt::to_bits_le)
#[cfg(target_pointer_width = "64")]
pub fn to_bits_be(&self) -> BitArray<BitArrayStore> {
let mut limbs = self.0.representative().limbs;
limbs.reverse();

BitArray::new(limbs)
}

/// Converts to big-endian bit representation.
/// This is as performant as [to_bits_le](Felt::to_bits_le)
#[cfg(all(feature = "alloc", not(target_pointer_width = "64")))]
pub fn to_bits_be(&self) -> BitArray<BitArrayStore> {
let mut limbs = self.0.representative().limbs;
limbs.reverse();

#[cfg(not(target_pointer_width = "64"))]
// Split limbs to adjust to BitArrayStore = [u32; 8]
let limbs: [u32; 8] = limbs
.into_iter()
.flat_map(|n| [(n >> 32) as u32, n as u32])
.collect::<Vec<u32>>()
.collect::<alloc::vec::Vec<u32>>()
.try_into()
.unwrap();

Expand All @@ -219,15 +232,24 @@ impl Felt {

/// Converts to little-endian bit representation.
/// This is as performant as [to_bits_be](Felt::to_bits_be)
#[cfg(target_pointer_width = "64")]
pub fn to_bits_le(&self) -> BitArray<BitArrayStore> {
let limbs = self.0.representative().limbs;

BitArray::new(limbs)
}

/// Converts to little-endian bit representation.
/// This is as performant as [to_bits_be](Felt::to_bits_be)
#[cfg(all(feature = "alloc", not(target_pointer_width = "64")))]
pub fn to_bits_le(&self) -> BitArray<BitArrayStore> {
let limbs = self.0.representative().limbs;

#[cfg(not(target_pointer_width = "64"))]
// Split limbs to adjust to BitArrayStore = [u32; 8]
let limbs: [u32; 8] = limbs
.into_iter()
.flat_map(|n| [n as u32, (n >> 32) as u32])
.collect::<Vec<u32>>()
.collect::<alloc::vec::Vec<u32>>()
.try_into()
.unwrap();

Expand Down Expand Up @@ -446,7 +468,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 @@ -458,7 +480,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 @@ -532,57 +554,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 @@ -874,10 +845,10 @@ mod arithmetic {
}

#[cfg(feature = "serde")]
mod serde {
use ::serde::{de, ser::SerializeSeq, Deserialize, Serialize};
mod serde_impl {
use alloc::{format, string::String};
use core::fmt;
use serde::{de, ser::SerializeSeq, Deserialize, Serialize};

use super::*;

Expand Down Expand Up @@ -940,7 +911,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 @@ -974,13 +945,13 @@ mod formatting {
}

/// Represents [Felt] in uppercase hexadecimal format.
#[cfg(feature = "alloc")]
impl fmt::UpperHex for Felt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"0x{}",
self.0
.to_string()
alloc::string::ToString::to_string(&self.0)
.strip_prefix("0x")
.unwrap()
.to_uppercase()
Expand Down Expand Up @@ -1025,8 +996,8 @@ mod errors {
#[cfg(test)]
mod test {
use super::alloc::{format, string::String, vec::Vec};
use super::felt_arbitrary::nonzero_felt;
use super::*;
use crate::felt_arbitrary::nonzero_felt;
use core::ops::Shl;
use proptest::prelude::*;
#[cfg(feature = "serde")]
Expand Down Expand Up @@ -1255,7 +1226,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 @@ -1336,21 +1307,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
Loading

0 comments on commit c7cf22e

Please sign in to comment.