Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impl num traits #18

Merged
merged 4 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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};
tdelabro marked this conversation as resolved.
Show resolved Hide resolved

#[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"))]
0xLucqs marked this conversation as resolved.
Show resolved Hide resolved
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>>()
0xLucqs marked this conversation as resolved.
Show resolved Hide resolved
.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