Skip to content

Commit

Permalink
Optimize/review numeric crate (#299)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
gaetbout authored May 13, 2024
1 parent c9995a4 commit ccd4677
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 71 deletions.
68 changes: 28 additions & 40 deletions src/numeric/src/integers.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use alexandria_math::BitShift;
pub trait UIntBytes<T> {
fn from_bytes(input: Span<u8>) -> Option<T>;
fn to_bytes(self: T) -> Span<u8>;
fn bytes_used(self: T) -> u8;
}

impl U32BytesImpl of UIntBytes<u32> {
Expand All @@ -13,23 +12,20 @@ impl U32BytesImpl of UIntBytes<u32> {
/// # Returns
/// * Option::Some(u32) if the operation succeeds
/// * Option::None otherwise
fn from_bytes(input: Span<u8>) -> Option<u32> {
fn from_bytes(mut input: Span<u8>) -> Option<u32> {
let len = input.len();
if len == 0 {
return Option::None;
}
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)
}

Expand All @@ -39,38 +35,30 @@ impl U32BytesImpl of UIntBytes<u32> {
/// # Returns
/// * The bytes array representation of the value.
fn to_bytes(mut self: u32) -> Span<u8> {
let bytes_used: u32 = self.bytes_used().into();
let mut bytes: Array<u8> = 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()
}
}
6 changes: 2 additions & 4 deletions src/numeric/src/interpolate.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 0 additions & 18 deletions src/numeric/src/tests/integers_test.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
22 changes: 13 additions & 9 deletions src/numeric/src/trapezoidal_rule.cairo
Original file line number Diff line number Diff line change
@@ -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).
Expand All @@ -20,21 +22,23 @@ pub fn trapezoidal_rule<
+Zero<T>,
+Into<u8, T>,
>(
xs: Span<T>, ys: Span<T>
mut xs: Span<T>, mut ys: Span<T>
) -> 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)
}

0 comments on commit ccd4677

Please sign in to comment.