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

feat: add fast trigonometric functions #243

Merged
merged 2 commits into from
Jan 23, 2024
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
6 changes: 6 additions & 0 deletions src/math/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Math

## [Fast trigonometric functions (sin, cos, tan)](./src/trigonometry.cairo)

The trigonometric functions are a set of mathematical functions that relate the angles of a triangle to the lengths of its sides. The most common trigonometric functions are sine, cosine, and tangent. These functions are used in many areas of mathematics, including geometry, calculus, and statistics. They are also used in physics, engineering, and other sciences.

Fast trigonometric functions are computational and spacial efficient, with minor errors compared to the standard trigonometric functions. Refer to http://hevi.info/tag/fast-sine-function/ for detailed information.

## [Aliquot sum](./src/aliquot_sum.cairo)

The aliquot sum algorithm calculates the sum of proper divisors of a given positive integer, providing insight into factors and divisors of a number, and classifying it as perfect, abundant, or deficient.
Expand Down
1 change: 1 addition & 0 deletions src/math/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod signed_u256;

#[cfg(test)]
mod tests;
mod trigonometry;
mod wad_ray_math;
mod zellers_congruence;
use integer::{
Expand Down
1 change: 1 addition & 0 deletions src/math/src/tests.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ mod sha256_test;
mod sha512_test;
mod signed_u256_test;
mod test_keccak256;
mod trigonometry_test;
mod wad_ray_math_test;
mod zellers_congruence_test;
57 changes: 57 additions & 0 deletions src/math/src/tests/trigonometry_test.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use alexandria_math::trigonometry::fast_cos;
use alexandria_math::trigonometry::fast_sin;
use alexandria_math::trigonometry::fast_tan;

#[test]
#[available_gas(200000)]
fn sin_positive_test_1() {
assert(fast_sin(3000000000) == 50000000, 'invalid result');
}

#[test]
#[available_gas(200000)]
fn sin_negative_test_1() {
assert(fast_sin(21000000000) == -50000000, 'invalid result');
}

#[test]
#[available_gas(200000)]
fn sin_positive_test_2() {
assert(fast_sin(3500000000) == 57367231, 'invalid result');
}

#[test]
#[available_gas(200000)]
fn sin_negative_test_2() {
assert(fast_sin(24300000000) == -89101846, 'invalid result');
}

#[test]
#[available_gas(200000)]
fn sin_positive_test_3() {
assert(fast_sin(75000000000) == 50000000, 'invalid result');
}

#[test]
#[available_gas(200000)]
fn cos_positive_test_1() {
assert(fast_cos(6000000000) == 50000000, 'invalid result');
}

#[test]
#[available_gas(200000)]
fn cos_negative_test_1() {
assert(fast_cos(12000000000) == -50000000, 'invalid result');
}

#[test]
#[available_gas(200000)]
fn tan_positive_test_1() {
assert(fast_tan(4500000000) == 100000000, 'invalid result');
}

#[test]
#[available_gas(200000)]
fn tan_negative_test_1() {
assert(fast_tan(-4500000000) == -100000000, 'invalid result');
}
141 changes: 141 additions & 0 deletions src/math/src/trigonometry.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use core::traits::TryInto;

// Calculate fast sin(x)
// Since there is no float in cairo, we multiply every number by 1e8
// # Arguments
// * `x` - The number to calculate sin(x)
// # Returns
// * `bool` - true if the result is positive, false if the result is negative
// * `u64` - sin(x) * 1e8
// # Example
// * fast_sin(3000000000) = (true, 50000000)
fn fast_sin_inner(x: u64) -> (bool, u64) {
let multipier = 100000000_u64;
let hollyst: u64 = 1745329_u64;
let sin_table = array![
0_u64, // sin(0)
17364818_u64, // sin(10)
34202014_u64, // sin(20)
50000000_u64, // sin(30)
64278761_u64, // sin(40)
76604444_u64, // sin(50)
86602540_u64, // sin(60)
93969262_u64, // sin(70)
98480775_u64, // sin(80)
100000000_u64 // sin(90)
];
let cos_table = array![
100000000_u64, // cos(0)
99984769_u64, // cos(1)
99939082_u64, // cos(2)
99862953_u64, // cos(3)
99756405_u64, // cos(4)
99619470_u64, // cos(5)
99452190_u64, // cos(6)
99254615_u64, // cos(7)
99026807_u64, // cos(8)
98768834_u64, // cos(9)
];

let mut a = x % consteval_int!(360_u64 * 100000000_u64);
let mut sig = true;
if a > consteval_int!(180_u64 * 100000000_u64) {
sig = false;
a = a - consteval_int!(180_u64 * 100000000_u64);
}

if a > consteval_int!(90_u64 * 100000000_u64) {
a = consteval_int!(180_u64 * 100000000_u64) - a;
}

let i: usize = (a / consteval_int!(10_u64 * 100000000_u64)).try_into().unwrap();
let j = a - i.into() * consteval_int!(10_u64 * 100000000_u64);
let int_j: usize = (j / multipier).try_into().unwrap();

let y = *sin_table[i] * *cos_table[int_j] / multipier
+ ((j * hollyst) / multipier) * *sin_table[9
- i] / multipier;

return (sig, y);
}

// Calculate fast sin(x)
// Since there is no float in cairo, we multiply every number by 1e8
// # Arguments
// * `x` - The number to calculate sin(x)
// # Returns
// * `i64` - sin(x) * 1e8
// # Example
// * fast_sin(3000000000) = 50000000
fn fast_sin(x: i64) -> i64 {
let mut a = x;
if x < 0 {
a = -x;
}
let input: u64 = a.try_into().unwrap();
let (sig_u64, result_u64) = fast_sin_inner(input);
let mut sig = sig_u64;
if x < 0 {
sig = !sig;
}
let result: i64 = result_u64.try_into().unwrap();
if sig {
return result;
} else {
return -result;
}
}

// Calculate fast cos(x)
// Since there is no float in cairo, we multiply every number by 1e8
// # Arguments
// * `x` - The number to calculate cos(x)
// # Returns
// * `i64` - cos(x) * 1e8
// # Example
// * fast_cos(6000000000) = 50000000
fn fast_cos(x: i64) -> i64 {
let mut a = x + consteval_int!(90_i64 * 100000000_i64);
if x < 0 {
a = -x;
}
let input: u64 = a.try_into().unwrap();
let (sig, result_u64) = fast_sin_inner(input);
let result: i64 = result_u64.try_into().unwrap();
if sig {
return result;
} else {
return -result;
}
}

// Calculate fast tan(x)
// Since there is no float in cairo, we multiply every number by 1e8
// # Arguments
// * `x` - The number to calculate tan(x)
// # Returns
// * `i64` - tan(x) * 1e8
// # Example
// * fast_tan(4500000000) = 100000000
fn fast_tan(x: i64) -> i64 {
let multipier = 100000000_u64;
let mut a = x;
if x < 0 {
a = -x;
}
let input_sin: u64 = a.try_into().unwrap();
let input_cos: u64 = (a + consteval_int!(90_i64 * 100000000_i64)).try_into().unwrap();
let (sig_sin, result_u64_sin) = fast_sin_inner(input_sin);
let (sig_cos, result_u64_cos) = fast_sin_inner(input_cos);
let mut sig = sig_sin || sig_cos;
if x < 0 {
sig = !sig;
}
let result_u64 = result_u64_sin * multipier / result_u64_cos;
let result: i64 = result_u64.try_into().unwrap();
if sig {
return result;
} else {
return -result;
}
}