Skip to content

Add DivRemGeneric trait #7722

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions corelib/src/num/traits.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
1 change: 1 addition & 0 deletions corelib/src/num/traits/ops.cairo
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod checked;
pub mod divrem;
pub mod overflowing;
pub mod pow;
pub mod saturating;
Expand Down
69 changes: 69 additions & 0 deletions corelib/src/num/traits/ops/divrem.cairo
Original file line number Diff line number Diff line change
@@ -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<U>`](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<u32> = 3.try_into().unwrap();
/// assert!(DivRem::<u32, u32>::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<u128> = u256_as_non_zero(10_u128.into()); // divisor
///
/// let (q, r) = DivRem::<u256, u128>::div_rem(big, nz10);
/// // q : u256, r : u128
/// ```
pub trait DivRem<T, U> {
/// 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<U>) -> (Self::Quotient, Self::Remainder);
}

// Compatibility bridge: DivRem<T> → DivRemGeneric<T,T>
mod by_divrem_legacy {
/// Generic adapter: if the old symmetric `DivRem<T>` exists,
/// provide the corresponding `DivRemGeneric<T,T>` implementation.
pub impl Bridge<T, +crate::traits::DivRem<T>> of super::DivRem<T, T> {
type Quotient = T;
type Remainder = T;

fn div_rem(lhs: T, rhs: NonZero<T>) -> (T, T) {
core::traits::DivRem::<T>::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<u8>;
pub impl BridgeU16 = by_divrem_legacy::Bridge<u16>;
pub impl BridgeU32 = by_divrem_legacy::Bridge<u32>;
pub impl BridgeU64 = by_divrem_legacy::Bridge<u64>;
pub impl BridgeU128 = by_divrem_legacy::Bridge<u128>;
pub impl BridgeU256 = by_divrem_legacy::Bridge<u256>;
51 changes: 26 additions & 25 deletions corelib/src/prelude/v2023_01.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions corelib/src/prelude/v2023_10.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)]
Expand Down
6 changes: 3 additions & 3 deletions corelib/src/prelude/v2024_07.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
58 changes: 57 additions & 1 deletion corelib/src/test/num_test.cairo
Original file line number Diff line number Diff line change
@@ -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]
Expand Down Expand Up @@ -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<u32> = 4_u32.try_into().unwrap();

let (q, r) = DivRem::<u32, u32>::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<u32> = 4_u32.try_into().unwrap();

let (q, r) = DivRemLegacy::<u32>::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<u128> = 6_u128.try_into().unwrap();

let (q, r) = DivRem::<u256, u128>::div_rem(lhs, rhs);
assert_eq!(q, 3_u256);
assert_eq!(r, 2_u128);
}

// Local impl so the test can call DivRem<u256,u128>.
impl U256ByU128DivRem of DivRem<u256, u128> {
type Quotient = u256;
type Remainder = u128;

fn div_rem(lhs: u256, rhs: NonZero<u128>) -> (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<u256> = rhs_u256.try_into().unwrap();
let (q, r_u256) = DivRemLegacy::<u256>::div_rem(lhs, rhs_u256_nz);
(q, r_u256.try_into().unwrap())
}
}
9 changes: 0 additions & 9 deletions corelib/src/traits.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,6 @@ pub trait RemEq<T> {
fn rem_eq(ref self: T, other: T);
}

// TODO(spapini): When associated types are supported, support the general trait DivRem<X, Y>.
/// Performs truncated division and remainder.
///
/// This trait provides a way to efficiently compute both the quotient and remainder in a single
Expand All @@ -377,13 +376,6 @@ pub trait RemEq<T> {
/// assert!(DivRem::div_rem(7_u32, 3) == (2, 1));
/// ```
pub trait DivRem<T> {
/// 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, T);
}

Expand Down Expand Up @@ -1133,4 +1125,3 @@ pub trait Felt252DictValue<T> {
#[must_use]
fn zero_default() -> T nopanic;
}

1 change: 1 addition & 0 deletions crates/cairo-lang-lowering/src/test_data/match
Original file line number Diff line number Diff line change
Expand Up @@ -1993,6 +1993,7 @@ Statements:
End:
Return(v4)


//! > ==========================================================================

//! > Test lower match missing args.
Expand Down