diff --git a/corelib/src/num/traits.cairo b/corelib/src/num/traits.cairo index 410274b073d..68b4a6971e5 100644 --- a/corelib/src/num/traits.cairo +++ b/corelib/src/num/traits.cairo @@ -13,6 +13,7 @@ pub use bounded::Bounded; #[feature("corelib-internal-use")] pub mod ops; pub use ops::checked::{CheckedAdd, CheckedMul, CheckedSub}; +pub use ops::divrem::DivRem; pub use ops::overflowing::{OverflowingAdd, OverflowingMul, OverflowingSub}; pub use ops::pow::Pow; pub use ops::saturating::{SaturatingAdd, SaturatingMul, SaturatingSub}; diff --git a/corelib/src/num/traits/ops.cairo b/corelib/src/num/traits/ops.cairo index b4b2a2334ae..d4e2cf686ca 100644 --- a/corelib/src/num/traits/ops.cairo +++ b/corelib/src/num/traits/ops.cairo @@ -1,4 +1,5 @@ pub mod checked; +pub mod divrem; pub mod overflowing; pub mod pow; pub mod saturating; diff --git a/corelib/src/num/traits/ops/divrem.cairo b/corelib/src/num/traits/ops/divrem.cairo new file mode 100644 index 00000000000..4377388b412 --- /dev/null +++ b/corelib/src/num/traits/ops/divrem.cairo @@ -0,0 +1,69 @@ +use crate::zeroable::NonZero; + +/// Performs truncated division **and** remainder. +/// +/// `T` – dividend type (left-hand operand) +/// `U` – divisor type (right-hand operand, must be wrapped in +/// [`NonZero`](core::num::non_zero::NonZero) at call-site) +/// +/// The division truncates toward zero, like Cairo’s `/` and `%`. +/// +/// # Associated types +/// * [`Quotient`] – the type produced by the division +/// * [`Remainder`] – the type produced by the modulo +/// +/// # Examples +/// +/// Identical operand types: +/// ```cairo +/// use core::traits::{DivRem, NonZero}; +/// +/// let lhs: u32 = 7; +/// let rhs: NonZero = 3.try_into().unwrap(); +/// assert!(DivRem::::div_rem(lhs, rhs) == (2, 1)); +/// ``` +/// +/// Heterogeneous division (`u256` by `u128`): +/// ```cairo +/// use core::traits::DivRem; +/// use integer::u256_as_non_zero; +/// +/// let big: u256 = 1_000_000; // dividend +/// let nz10: NonZero = u256_as_non_zero(10_u128.into()); // divisor +/// +/// let (q, r) = DivRem::::div_rem(big, nz10); +/// // q : u256, r : u128 +/// ``` +pub trait DivRem { + /// Quotient returned by the division. + type Quotient; + + /// Remainder returned by the modulo operation. + type Remainder; + + /// Computes both `/` and `%` in a single pass. + fn div_rem(lhs: T, rhs: NonZero) -> (Self::Quotient, Self::Remainder); +} + +// Compatibility bridge: DivRem → DivRemGeneric +mod by_divrem_legacy { + /// Generic adapter: if the old symmetric `DivRem` exists, + /// provide the corresponding `DivRemGeneric` implementation. + pub impl Bridge> of super::DivRem { + type Quotient = T; + type Remainder = T; + + fn div_rem(lhs: T, rhs: NonZero) -> (T, T) { + core::traits::DivRem::::div_rem(lhs, rhs) + } + } +} + +// Instantiate the generic adapter for every concrete integer type +// that already has a symmetric `DivRem` implementation. +pub impl BridgeU8 = by_divrem_legacy::Bridge; +pub impl BridgeU16 = by_divrem_legacy::Bridge; +pub impl BridgeU32 = by_divrem_legacy::Bridge; +pub impl BridgeU64 = by_divrem_legacy::Bridge; +pub impl BridgeU128 = by_divrem_legacy::Bridge; +pub impl BridgeU256 = by_divrem_legacy::Bridge; diff --git a/corelib/src/prelude/v2023_01.cairo b/corelib/src/prelude/v2023_01.cairo index 68e2c933580..d7efccf2242 100644 --- a/corelib/src/prelude/v2023_01.cairo +++ b/corelib/src/prelude/v2023_01.cairo @@ -2,45 +2,45 @@ pub use starknet::storage::{ StorageMapReadAccess, StorageMapWriteAccess, StoragePointerReadAccess, StoragePointerWriteAccess, }; -use crate::array::{self, Array, ArrayTrait, Span, SpanTrait, ToSpanTrait}; -use crate::box::{self, Box, BoxTrait}; +use crate::array::{Array, ArrayTrait, Span, SpanTrait, ToSpanTrait, self}; +use crate::box::{Box, BoxTrait, self}; use crate::byte_array::{ - self, ByteArray, ByteArrayIndexView, ByteArrayStringLiteral, ByteArrayTrait, + ByteArray, ByteArrayIndexView, ByteArrayStringLiteral, ByteArrayTrait, self, }; use crate::bytes_31::{ - self, Bytes31IndexView, Bytes31IntoFelt252, Bytes31Trait, Felt252TryIntoBytes31, bytes31, - bytes31_const, + Bytes31IndexView, Bytes31IntoFelt252, Bytes31Trait, Felt252TryIntoBytes31, bytes31, + bytes31_const, self, }; -use crate::clone::{self, Clone}; +use crate::clone::{Clone, self}; use crate::dict::{ - self, Felt252Dict, Felt252DictTrait, SquashedFelt252Dict, felt252_dict_new, felt252_dict_squash, + Felt252Dict, Felt252DictTrait, SquashedFelt252Dict, felt252_dict_new, felt252_dict_squash, self, }; -use crate::ec::{self, EcOp, EcPoint, EcState}; -use crate::gas::{self, BuiltinCosts, GasBuiltin, get_builtin_costs}; +use crate::ec::{EcOp, EcPoint, EcState, self}; +use crate::gas::{BuiltinCosts, GasBuiltin, get_builtin_costs, self}; use crate::integer::{ - self, Bitwise, Felt252IntoU256, Felt252TryIntoU128, Felt252TryIntoU16, Felt252TryIntoU32, + Bitwise, Felt252IntoU256, Felt252TryIntoU128, Felt252TryIntoU16, Felt252TryIntoU32, Felt252TryIntoU64, Felt252TryIntoU8, I128IntoFelt252, I16IntoFelt252, I32IntoFelt252, I64IntoFelt252, I8IntoFelt252, NumericLiteral, U128IntoFelt252, U16IntoFelt252, U32IntoFelt252, - U64IntoFelt252, U8IntoFelt252, i128, i16, i32, i64, i8, u128, u128_is_zero, u16, u256, u32, u64, - u8, + U64IntoFelt252, U8IntoFelt252, i128, i16, i32, i64, i8, self, u128, u128_is_zero, u16, u256, + u32, u64, u8, }; #[feature("corelib-internal-use")] #[deprecated(feature: "corelib-internal-use", note: "Use `core::num::traits::Sqrt` instead")] use crate::integer::{u128_sqrt, u256_sqrt}; use crate::iter::{FromIterator, IntoIterator, Iterator}; -use crate::nullable::{self, Nullable, NullableTrait, match_nullable, null, nullable_from_box}; +use crate::nullable::{Nullable, NullableTrait, match_nullable, null, nullable_from_box, self}; pub use crate::ops::Deref; -use crate::option::Option::{self, None, Some}; -use crate::option::{self, OptionTrait}; -use crate::panics::{self, Panic, PanicResult, panic}; -use crate::pedersen::{self, Pedersen}; -use crate::poseidon::{self, Poseidon}; -use crate::result::Result::{self, Err, Ok}; -use crate::result::{self, ResultTrait}; -use crate::serde::{self, Serde}; +use crate::option::Option::{None, Some, self}; +use crate::option::{OptionTrait, self}; +use crate::panics::{Panic, PanicResult, panic, self}; +use crate::pedersen::{Pedersen, self}; +use crate::poseidon::{Poseidon, self}; +use crate::result::Result::{Err, Ok, self}; +use crate::result::{ResultTrait, self}; +use crate::serde::{Serde, self}; #[feature("corelib-internal-use")] -use crate::starknet::{self, System}; -use crate::string::{self, StringLiteral}; +use crate::starknet::{System, self}; +use crate::string::{StringLiteral, self}; #[cfg(test)] use crate::test; #[deprecated( @@ -69,12 +69,13 @@ pub use crate::traits::RemEq; #[feature("deprecated-op-assign-traits")] pub use crate::traits::SubEq; use crate::traits::{ - self, Add, BitAnd, BitNot, BitOr, BitXor, Copy, Default, Destruct, Div, DivRem, Drop, + Add, BitAnd, BitNot, BitOr, BitXor, Copy, Default, Destruct, Div, DivRem, Drop, Felt252DictValue, Into, Mul, Neg, Not, PanicDestruct, PartialEq, PartialOrd, Rem, Sub, TryInto, + self, }; #[feature("deprecated-index-traits")] use crate::traits::{Index, IndexView}; -use crate::zeroable::{self, NonZero, Zeroable}; +use crate::zeroable::{NonZero, Zeroable, self}; use crate::{ BoolBitAnd, BoolBitOr, BoolBitXor, BoolFelt252DictValue, BoolIntoFelt252, BoolNot, BoolPartialEq, BoolSerde, Felt252Add, Felt252AddEq, Felt252Default, Felt252Felt252DictValue, diff --git a/corelib/src/prelude/v2023_10.cairo b/corelib/src/prelude/v2023_10.cairo index 657e0c50c61..905a52fb293 100644 --- a/corelib/src/prelude/v2023_10.cairo +++ b/corelib/src/prelude/v2023_10.cairo @@ -13,16 +13,16 @@ pub use crate::integer::{Bitwise, i128, i16, i32, i64, i8, u128, u16, u256, u32, pub use crate::iter::{FromIterator, IntoIterator, Iterator}; pub use crate::nullable::{Nullable, NullableTrait}; pub use crate::ops::Deref; -pub use crate::option::Option::{self, None, Some}; +pub use crate::option::Option::{None, Some, self}; pub use crate::option::OptionTrait; pub use crate::panics::{Panic, PanicResult, panic}; pub use crate::pedersen::Pedersen; pub use crate::poseidon::Poseidon; -pub use crate::result::Result::{self, Err, Ok}; +pub use crate::result::Result::{Err, Ok, self}; pub use crate::result::ResultTrait; pub use crate::serde::Serde; #[feature("corelib-internal-use")] -pub use crate::starknet::{self, System}; +pub use crate::starknet::{System, self}; #[deprecated( feature: "deprecated-op-assign-traits", note: "Use `core::ops::AddAssign`.", since: "2.7.0", )] diff --git a/corelib/src/prelude/v2024_07.cairo b/corelib/src/prelude/v2024_07.cairo index 0c695b35599..3b98db2ae9d 100644 --- a/corelib/src/prelude/v2024_07.cairo +++ b/corelib/src/prelude/v2024_07.cairo @@ -8,14 +8,14 @@ pub use crate::integer::{i128, i16, i32, i64, i8, u128, u16, u256, u32, u64, u8} pub use crate::iter::{FromIterator, IntoIterator, Iterator}; pub use crate::nullable::{Nullable, NullableTrait}; pub use crate::ops::Deref; -pub use crate::option::Option::{self, None, Some}; +pub use crate::option::Option::{None, Some, self}; pub use crate::option::OptionTrait; pub use crate::panics::{Panic, PanicResult, panic}; -pub use crate::result::Result::{self, Err, Ok}; +pub use crate::result::Result::{Err, Ok, self}; pub use crate::result::ResultTrait; pub use crate::serde::Serde; #[feature("corelib-internal-use")] -pub use crate::starknet::{self, System}; +pub use crate::starknet::{System, self}; pub use crate::traits::{ Add, Copy, Default, Destruct, Div, DivRem, Drop, Felt252DictValue, Into, Mul, Neg, Not, PanicDestruct, PartialEq, PartialOrd, Rem, Sub, TryInto, diff --git a/corelib/src/test/num_test.cairo b/corelib/src/test/num_test.cairo index 9530b1f8695..752dd22caca 100644 --- a/corelib/src/test/num_test.cairo +++ b/corelib/src/test/num_test.cairo @@ -1,8 +1,10 @@ use crate::num::traits::{ - BitSize, Bounded, CheckedAdd, CheckedMul, CheckedSub, OverflowingAdd, OverflowingMul, + BitSize, Bounded, CheckedAdd, CheckedMul, CheckedSub, DivRem, OverflowingAdd, OverflowingMul, OverflowingSub, Pow, SaturatingAdd, SaturatingMul, SaturatingSub, WrappingAdd, WrappingMul, WrappingSub, }; +use crate::traits::DivRem as DivRemLegacy; +use crate::zeroable::NonZero; #[test] @@ -467,3 +469,57 @@ fn test_pow() { assert_eq!(2.pow(9), 0b1000000000); assert_eq!(2.pow(10), 0b10000000000); } + + +// Bridge-driven generic division: u32 / u32 +#[test] +fn test_divrem_generic_u32() { + let lhs: u32 = 27; + let rhs: NonZero = 4_u32.try_into().unwrap(); + + let (q, r) = DivRem::::div_rem(lhs, rhs); + + assert_eq!(q, 6); + assert_eq!(r, 3); +} + + +// Legacy API still works +#[test] +fn test_divrem_legacy_u32() { + let lhs: u32 = 27; + let rhs: NonZero = 4_u32.try_into().unwrap(); + + let (q, r) = DivRemLegacy::::div_rem(lhs, rhs); + + assert_eq!(q, 6); + assert_eq!(r, 3); +} + +#[test] +fn test_divrem_u256_by_u128() { + // Heterogeneous example: u256 / u128. + // We supply a one-off impl in the test so corelib proper remains minimal. + + let lhs: u256 = 20; + let rhs: NonZero = 6_u128.try_into().unwrap(); + + let (q, r) = DivRem::::div_rem(lhs, rhs); + assert_eq!(q, 3_u256); + assert_eq!(r, 2_u128); +} + +// Local impl so the test can call DivRem. +impl U256ByU128DivRem of DivRem { + type Quotient = u256; + type Remainder = u128; + + fn div_rem(lhs: u256, rhs: NonZero) -> (u256, u128) { + // Reuse the old symmetric trait on (u256,u256). + let rhs_u128: u128 = rhs.into(); + let rhs_u256: u256 = rhs_u128.into(); + let rhs_u256_nz: NonZero = rhs_u256.try_into().unwrap(); + let (q, r_u256) = DivRemLegacy::::div_rem(lhs, rhs_u256_nz); + (q, r_u256.try_into().unwrap()) + } +} diff --git a/corelib/src/traits.cairo b/corelib/src/traits.cairo index b4d6d22e8d8..e6a52572fe8 100644 --- a/corelib/src/traits.cairo +++ b/corelib/src/traits.cairo @@ -364,7 +364,6 @@ pub trait RemEq { fn rem_eq(ref self: T, other: T); } -// TODO(spapini): When associated types are supported, support the general trait DivRem. /// Performs truncated division and remainder. /// /// This trait provides a way to efficiently compute both the quotient and remainder in a single @@ -377,13 +376,6 @@ pub trait RemEq { /// assert!(DivRem::div_rem(7_u32, 3) == (2, 1)); /// ``` pub trait DivRem { - /// Performs the `/` and the `%` operations, returning both the quotient and remainder. - /// - /// # Examples - /// - /// ``` - /// assert!(DivRem::div_rem(12_u32, 10) == (1, 2)); - /// ``` fn div_rem(lhs: T, rhs: NonZero) -> (T, T); } @@ -1133,4 +1125,3 @@ pub trait Felt252DictValue { #[must_use] fn zero_default() -> T nopanic; } - diff --git a/crates/cairo-lang-lowering/src/test_data/match b/crates/cairo-lang-lowering/src/test_data/match index 8dfc22551bf..0ff1f56c44a 100644 --- a/crates/cairo-lang-lowering/src/test_data/match +++ b/crates/cairo-lang-lowering/src/test_data/match @@ -1993,6 +1993,7 @@ Statements: End: Return(v4) + //! > ========================================================================== //! > Test lower match missing args.