From ccd46777c4f1e4298c33cd7ec11c39484d9030b8 Mon Sep 17 00:00:00 2001 From: gaetbout Date: Mon, 13 May 2024 11:41:24 +0200 Subject: [PATCH] Optimize/review numeric crate (#299) ## Pull Request type Please check the type of change your PR introduces: - [ ] Bugfix - [ ] Feature - [ ] Code style update (formatting, renaming) - [x] Refactoring (no functional changes, no API changes) - [ ] Build-related changes - [ ] Documentation content changes - [ ] Other (please describe): ## What is the new behavior? Optimized files in numeric folder --- src/numeric/src/integers.cairo | 68 ++++++++++------------- src/numeric/src/interpolate.cairo | 6 +- src/numeric/src/tests/integers_test.cairo | 18 ------ src/numeric/src/trapezoidal_rule.cairo | 22 +++++--- 4 files changed, 43 insertions(+), 71 deletions(-) diff --git a/src/numeric/src/integers.cairo b/src/numeric/src/integers.cairo index 286cbf2e..e88f2c6d 100644 --- a/src/numeric/src/integers.cairo +++ b/src/numeric/src/integers.cairo @@ -3,7 +3,6 @@ use alexandria_math::BitShift; pub trait UIntBytes { fn from_bytes(input: Span) -> Option; fn to_bytes(self: T) -> Span; - fn bytes_used(self: T) -> u8; } impl U32BytesImpl of UIntBytes { @@ -13,7 +12,7 @@ impl U32BytesImpl of UIntBytes { /// # Returns /// * Option::Some(u32) if the operation succeeds /// * Option::None otherwise - fn from_bytes(input: Span) -> Option { + fn from_bytes(mut input: Span) -> Option { let len = input.len(); if len == 0 { return Option::None; @@ -21,15 +20,12 @@ impl U32BytesImpl of UIntBytes { if len > 4 { return Option::None; } - let offset: u32 = len - 1; let mut result: u32 = 0; - let mut i: u32 = 0; - while i != len { - let byte: u32 = (*input[i]).into(); - result += BitShift::shl(byte, 8 * (offset - i)); - - i += 1; - }; + while let Option::Some(byte) = input + .pop_front() { + let byte: u32 = (*byte).into(); + result = result * 0x100 + byte; + }; Option::Some(result) } @@ -39,38 +35,30 @@ impl U32BytesImpl of UIntBytes { /// # Returns /// * The bytes array representation of the value. fn to_bytes(mut self: u32) -> Span { - let bytes_used: u32 = self.bytes_used().into(); - let mut bytes: Array = Default::default(); - let mut i = 0; - while i != bytes_used { - let val = BitShift::shr(self, 8 * (bytes_used.try_into().unwrap() - i - 1)); - bytes.append((val & 0xFF).try_into().unwrap()); - i += 1; - }; + let val0: u8 = (self & 0xFF).try_into().unwrap(); + let val1 = self & 0xFF00; + let val2 = self & 0xFF0000; + let val3 = self & 0xFF000000; + if val3 != 0 { + return array![ + (val3 / 0x1000000).try_into().unwrap(), + (val2 / 0x10000).try_into().unwrap(), + (val1 / 0x100).try_into().unwrap(), + val0 + ] + .span(); + } - bytes.span() - } + if val2 != 0 { + return array![ + (val2 / 0x10000).try_into().unwrap(), (val1 / 0x100).try_into().unwrap(), val0 + ] + .span(); + } - /// Returns the number of bytes used to represent a `u32` value. - /// # Arguments - /// * `self` - The value to check. - /// # Returns - /// The number of bytes used to represent the value. - fn bytes_used(self: u32) -> u8 { - if self < 0x10000 { // 256^2 - if self < 0x100 { // 256^1 - if self == 0 { - return 0; - } else { - return 1; - }; - } - return 2; - } else { - if self < 0x1000000 { // 256^6 - return 3; - } - return 4; + if val1 != 0 { + return array![(val1 / 0x100).try_into().unwrap(), val0].span(); } + array![val0].span() } } diff --git a/src/numeric/src/interpolate.cairo b/src/numeric/src/interpolate.cairo index 2477aa30..6303da1e 100644 --- a/src/numeric/src/interpolate.cairo +++ b/src/numeric/src/interpolate.cairo @@ -100,18 +100,16 @@ pub fn interpolate_fast< // [Check] Extrapolation if x <= *xs[0] { - let y = match extrapolation { + return match extrapolation { Extrapolation::Null => Zero::zero(), Extrapolation::Constant => *ys[0], }; - return y; } if x >= *xs[xs.len() - 1] { - let y = match extrapolation { + return match extrapolation { Extrapolation::Null => Zero::zero(), Extrapolation::Constant => *ys[xs.len() - 1], }; - return y; } // [Compute] Interpolation with binary search diff --git a/src/numeric/src/tests/integers_test.cairo b/src/numeric/src/tests/integers_test.cairo index 6bcba438..0f0914ee 100644 --- a/src/numeric/src/tests/integers_test.cairo +++ b/src/numeric/src/tests/integers_test.cairo @@ -56,21 +56,3 @@ fn test_u32_to_bytes_leading_zeros() { assert_eq!(*res[0], 0xf4, "wrong result value"); assert_eq!(*res[1], 0x32, "wrong result value"); } - -#[test] -#[available_gas(20000000)] -fn test_u32_bytes_used() { - let len: u32 = 0x1234; - let bytes_count = len.bytes_used(); - - assert_eq!(bytes_count, 2, "wrong bytes count"); -} - -#[test] -#[available_gas(20000000)] -fn test_u32_bytes_used_leading_zeroes() { - let len: u32 = 0x001234; - let bytes_count = len.bytes_used(); - - assert_eq!(bytes_count, 2, "wrong bytes count"); -} diff --git a/src/numeric/src/trapezoidal_rule.cairo b/src/numeric/src/trapezoidal_rule.cairo index 8d86aa7d..5856ff75 100644 --- a/src/numeric/src/trapezoidal_rule.cairo +++ b/src/numeric/src/trapezoidal_rule.cairo @@ -1,4 +1,6 @@ +use core::array::SpanTrait; use core::num::traits::Zero; +use core::option::OptionTrait; //! Integrate using the composite trapezoidal rule /// Integrate y(x). @@ -20,21 +22,23 @@ pub fn trapezoidal_rule< +Zero, +Into, >( - xs: Span, ys: Span + mut xs: Span, mut ys: Span ) -> T { // [Check] Inputs assert(xs.len() == ys.len(), 'Arrays must have the same len'); assert(xs.len() >= 2, 'Array must have at least 2 elts'); // [Compute] Trapezoidal rule - let mut index = 0; + let mut prev_x = *xs.pop_front().unwrap(); + let mut prev_y = *ys.pop_front().unwrap(); let mut value = Zero::zero(); - while index - + 1 != xs - .len() { - assert(*xs[index + 1] > *xs[index], 'Abscissa must be sorted'); - value += (*xs[index + 1] - *xs[index]) * (*ys[index] + *ys[index + 1]); - index += 1; - }; + while let Option::Some(next_x) = xs + .pop_front() { + assert(*next_x > prev_x, 'Abscissa must be sorted'); + let next_y = *ys.pop_front().unwrap(); + value += (*next_x - prev_x) * (prev_y + next_y); + prev_x = *next_x; + prev_y = next_y; + }; value / Into::into(2_u8) }