diff --git a/Scarb.lock b/Scarb.lock index ee9a715a..0cd56aae 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -50,6 +50,9 @@ version = "0.2.1" [[package]] name = "alexandria_merkle_tree" version = "0.1.0" +dependencies = [ + "alexandria_math", +] [[package]] name = "alexandria_numeric" diff --git a/packages/bytes/src/utils.cairo b/packages/bytes/src/utils.cairo index 0283fe68..acc6b422 100644 --- a/packages/bytes/src/utils.cairo +++ b/packages/bytes/src/utils.cairo @@ -1,4 +1,5 @@ use alexandria_bytes::{Bytes, BytesTrait}; +use alexandria_math::const_pow::pow2; use alexandria_data_structures::array_ext::ArrayTraitExt; use core::fmt::{Debug, Display, Formatter, Error}; use core::integer::u128_byte_reverse; @@ -94,7 +95,7 @@ fn keccak_add_uint128_be(ref keccak_input: Array::, value: u128, value_size keccak_input.append(reversed_value.try_into().unwrap()); } else { let (high, low) = DivRem::div_rem( - reversed_value, u128_fast_pow2(64).try_into().expect('Division by 0') + reversed_value, pow2(64).try_into().expect('Division by 0') ); keccak_input.append(low.try_into().unwrap()); keccak_input.append(high.try_into().unwrap()); @@ -209,7 +210,7 @@ pub fn u128_split(value: u128, value_size: usize, left_size: usize) -> (u128, u1 if left_size == 0 { (0, value) } else { - let power = u128_fast_pow2((value_size - left_size) * 8); + let power = pow2((value_size - left_size) * 8); DivRem::div_rem(value, power.try_into().expect('Division by 0')) } } @@ -253,7 +254,7 @@ pub fn read_sub_u128(value: u128, value_size: usize, offset: usize, size: usize) pub fn u128_join(left: u128, right: u128, right_size: usize) -> u128 { let left_size = u128_bytes_len(left); assert(left_size + right_size <= 16, 'left shift overflow'); - let shift = u128_fast_pow2(right_size * 8); + let shift = pow2(right_size * 8); left * shift + right } @@ -295,266 +296,3 @@ fn u128_bytes_len(value: u128) -> usize { 16_usize } } - -/// return 2^exp where exp in [0, 127] -fn u128_fast_pow2(exp: usize) -> u128 { - assert(exp <= 127, 'invalid exp'); - - if exp == 0_usize { - 1_u128 - } else if exp == 1_usize { - 2_u128 - } else if exp == 2_usize { - 4_u128 - } else if exp == 3_usize { - 8_u128 - } else if exp == 4_usize { - 16_u128 - } else if exp == 5_usize { - 32_u128 - } else if exp == 6_usize { - 64_u128 - } else if exp == 7_usize { - 128_u128 - } else if exp == 8_usize { - 256_u128 - } else if exp == 9_usize { - 512_u128 - } else if exp == 10_usize { - 1024_u128 - } else if exp == 11_usize { - 2048_u128 - } else if exp == 12_usize { - 4096_u128 - } else if exp == 13_usize { - 8192_u128 - } else if exp == 14_usize { - 16384_u128 - } else if exp == 15_usize { - 32768_u128 - } else if exp == 16_usize { - 65536_u128 - } else if exp == 17_usize { - 131072_u128 - } else if exp == 18_usize { - 262144_u128 - } else if exp == 19_usize { - 524288_u128 - } else if exp == 20_usize { - 1048576_u128 - } else if exp == 21_usize { - 2097152_u128 - } else if exp == 22_usize { - 4194304_u128 - } else if exp == 23_usize { - 8388608_u128 - } else if exp == 24_usize { - 16777216_u128 - } else if exp == 25_usize { - 33554432_u128 - } else if exp == 26_usize { - 67108864_u128 - } else if exp == 27_usize { - 134217728_u128 - } else if exp == 28_usize { - 268435456_u128 - } else if exp == 29_usize { - 536870912_u128 - } else if exp == 30_usize { - 1073741824_u128 - } else if exp == 31_usize { - 2147483648_u128 - } else if exp == 32_usize { - 4294967296_u128 - } else if exp == 33_usize { - 8589934592_u128 - } else if exp == 34_usize { - 17179869184_u128 - } else if exp == 35_usize { - 34359738368_u128 - } else if exp == 36_usize { - 68719476736_u128 - } else if exp == 37_usize { - 137438953472_u128 - } else if exp == 38_usize { - 274877906944_u128 - } else if exp == 39_usize { - 549755813888_u128 - } else if exp == 40_usize { - 1099511627776_u128 - } else if exp == 41_usize { - 2199023255552_u128 - } else if exp == 42_usize { - 4398046511104_u128 - } else if exp == 43_usize { - 8796093022208_u128 - } else if exp == 44_usize { - 17592186044416_u128 - } else if exp == 45_usize { - 35184372088832_u128 - } else if exp == 46_usize { - 70368744177664_u128 - } else if exp == 47_usize { - 140737488355328_u128 - } else if exp == 48_usize { - 281474976710656_u128 - } else if exp == 49_usize { - 562949953421312_u128 - } else if exp == 50_usize { - 1125899906842624_u128 - } else if exp == 51_usize { - 2251799813685248_u128 - } else if exp == 52_usize { - 4503599627370496_u128 - } else if exp == 53_usize { - 9007199254740992_u128 - } else if exp == 54_usize { - 18014398509481984_u128 - } else if exp == 55_usize { - 36028797018963968_u128 - } else if exp == 56_usize { - 72057594037927936_u128 - } else if exp == 57_usize { - 144115188075855872_u128 - } else if exp == 58_usize { - 288230376151711744_u128 - } else if exp == 59_usize { - 576460752303423488_u128 - } else if exp == 60_usize { - 1152921504606846976_u128 - } else if exp == 61_usize { - 2305843009213693952_u128 - } else if exp == 62_usize { - 4611686018427387904_u128 - } else if exp == 63_usize { - 9223372036854775808_u128 - } else if exp == 64_usize { - 18446744073709551616_u128 - } else if exp == 65_usize { - 36893488147419103232_u128 - } else if exp == 66_usize { - 73786976294838206464_u128 - } else if exp == 67_usize { - 147573952589676412928_u128 - } else if exp == 68_usize { - 295147905179352825856_u128 - } else if exp == 69_usize { - 590295810358705651712_u128 - } else if exp == 70_usize { - 1180591620717411303424_u128 - } else if exp == 71_usize { - 2361183241434822606848_u128 - } else if exp == 72_usize { - 4722366482869645213696_u128 - } else if exp == 73_usize { - 9444732965739290427392_u128 - } else if exp == 74_usize { - 18889465931478580854784_u128 - } else if exp == 75_usize { - 37778931862957161709568_u128 - } else if exp == 76_usize { - 75557863725914323419136_u128 - } else if exp == 77_usize { - 151115727451828646838272_u128 - } else if exp == 78_usize { - 302231454903657293676544_u128 - } else if exp == 79_usize { - 604462909807314587353088_u128 - } else if exp == 80_usize { - 1208925819614629174706176_u128 - } else if exp == 81_usize { - 2417851639229258349412352_u128 - } else if exp == 82_usize { - 4835703278458516698824704_u128 - } else if exp == 83_usize { - 9671406556917033397649408_u128 - } else if exp == 84_usize { - 19342813113834066795298816_u128 - } else if exp == 85_usize { - 38685626227668133590597632_u128 - } else if exp == 86_usize { - 77371252455336267181195264_u128 - } else if exp == 87_usize { - 154742504910672534362390528_u128 - } else if exp == 88_usize { - 309485009821345068724781056_u128 - } else if exp == 89_usize { - 618970019642690137449562112_u128 - } else if exp == 90_usize { - 1237940039285380274899124224_u128 - } else if exp == 91_usize { - 2475880078570760549798248448_u128 - } else if exp == 92_usize { - 4951760157141521099596496896_u128 - } else if exp == 93_usize { - 9903520314283042199192993792_u128 - } else if exp == 94_usize { - 19807040628566084398385987584_u128 - } else if exp == 95_usize { - 39614081257132168796771975168_u128 - } else if exp == 96_usize { - 79228162514264337593543950336_u128 - } else if exp == 97_usize { - 158456325028528675187087900672_u128 - } else if exp == 98_usize { - 316912650057057350374175801344_u128 - } else if exp == 99_usize { - 633825300114114700748351602688_u128 - } else if exp == 100_usize { - 1267650600228229401496703205376_u128 - } else if exp == 101_usize { - 2535301200456458802993406410752_u128 - } else if exp == 102_usize { - 5070602400912917605986812821504_u128 - } else if exp == 103_usize { - 10141204801825835211973625643008_u128 - } else if exp == 104_usize { - 20282409603651670423947251286016_u128 - } else if exp == 105_usize { - 40564819207303340847894502572032_u128 - } else if exp == 106_usize { - 81129638414606681695789005144064_u128 - } else if exp == 107_usize { - 162259276829213363391578010288128_u128 - } else if exp == 108_usize { - 324518553658426726783156020576256_u128 - } else if exp == 109_usize { - 649037107316853453566312041152512_u128 - } else if exp == 110_usize { - 1298074214633706907132624082305024_u128 - } else if exp == 111_usize { - 2596148429267413814265248164610048_u128 - } else if exp == 112_usize { - 5192296858534827628530496329220096_u128 - } else if exp == 113_usize { - 10384593717069655257060992658440192_u128 - } else if exp == 114_usize { - 20769187434139310514121985316880384_u128 - } else if exp == 115_usize { - 41538374868278621028243970633760768_u128 - } else if exp == 116_usize { - 83076749736557242056487941267521536_u128 - } else if exp == 117_usize { - 166153499473114484112975882535043072_u128 - } else if exp == 118_usize { - 332306998946228968225951765070086144_u128 - } else if exp == 119_usize { - 664613997892457936451903530140172288_u128 - } else if exp == 120_usize { - 1329227995784915872903807060280344576_u128 - } else if exp == 121_usize { - 2658455991569831745807614120560689152_u128 - } else if exp == 122_usize { - 5316911983139663491615228241121378304_u128 - } else if exp == 123_usize { - 10633823966279326983230456482242756608_u128 - } else if exp == 124_usize { - 21267647932558653966460912964485513216_u128 - } else if exp == 125_usize { - 42535295865117307932921825928971026432_u128 - } else if exp == 126_usize { - 85070591730234615865843651857942052864_u128 - } else { - 170141183460469231731687303715884105728_u128 - } -} diff --git a/packages/math/src/armstrong_number.cairo b/packages/math/src/armstrong_number.cairo index 9d25314e..d7189571 100644 --- a/packages/math/src/armstrong_number.cairo +++ b/packages/math/src/armstrong_number.cairo @@ -15,7 +15,7 @@ pub fn is_armstrong_number(mut num: u128) -> bool { } let lastDigit = num % 10; - let sum = pow(lastDigit, digits); + let sum = pow(lastDigit, digits.into()); num = num / 10; if sum > original_num { break false; diff --git a/packages/math/src/const_pow.cairo b/packages/math/src/const_pow.cairo new file mode 100644 index 00000000..9f76b3db --- /dev/null +++ b/packages/math/src/const_pow.cairo @@ -0,0 +1,565 @@ +/// Calculate 2 raised to the power of the given exponent +/// using a pre-computed lookup table +/// # Arguments +/// * `exponent` - The exponent to raise 2 to +/// # Returns +/// * `u256` - The result of 2^exponent +/// # Panics +/// * If `exponent` is greater than 255 (out of the supported range) +pub fn pow2_u256(exponent: u32) -> u256 { + if exponent < 128 { + pow2(exponent).into() + } else { + u256 { low: 0, high: pow2(exponent - 128), } + } +} + +/// Calculate 2 raised to the power of the given exponent +/// using a pre-computed lookup table +/// # Arguments +/// * `exponent` - The exponent to raise 2 to +/// # Returns +/// * `u128` - The result of 2^exponent +/// # Panics +/// * If `exponent` is greater than 127 (out of the supported range) +pub fn pow2(exponent: u32) -> u128 { + let hardcoded_results: [u128; 128] = [ + 0x1, + 0x2, + 0x4, + 0x8, + 0x10, + 0x20, + 0x40, + 0x80, + 0x100, + 0x200, + 0x400, + 0x800, + 0x1000, + 0x2000, + 0x4000, + 0x8000, + 0x10000, + 0x20000, + 0x40000, + 0x80000, + 0x100000, + 0x200000, + 0x400000, + 0x800000, + 0x1000000, + 0x2000000, + 0x4000000, + 0x8000000, + 0x10000000, + 0x20000000, + 0x40000000, + 0x80000000, + 0x100000000, + 0x200000000, + 0x400000000, + 0x800000000, + 0x1000000000, + 0x2000000000, + 0x4000000000, + 0x8000000000, + 0x10000000000, + 0x20000000000, + 0x40000000000, + 0x80000000000, + 0x100000000000, + 0x200000000000, + 0x400000000000, + 0x800000000000, + 0x1000000000000, + 0x2000000000000, + 0x4000000000000, + 0x8000000000000, + 0x10000000000000, + 0x20000000000000, + 0x40000000000000, + 0x80000000000000, + 0x100000000000000, + 0x200000000000000, + 0x400000000000000, + 0x800000000000000, + 0x1000000000000000, + 0x2000000000000000, + 0x4000000000000000, + 0x8000000000000000, + 0x10000000000000000, + 0x20000000000000000, + 0x40000000000000000, + 0x80000000000000000, + 0x100000000000000000, + 0x200000000000000000, + 0x400000000000000000, + 0x800000000000000000, + 0x1000000000000000000, + 0x2000000000000000000, + 0x4000000000000000000, + 0x8000000000000000000, + 0x10000000000000000000, + 0x20000000000000000000, + 0x40000000000000000000, + 0x80000000000000000000, + 0x100000000000000000000, + 0x200000000000000000000, + 0x400000000000000000000, + 0x800000000000000000000, + 0x1000000000000000000000, + 0x2000000000000000000000, + 0x4000000000000000000000, + 0x8000000000000000000000, + 0x10000000000000000000000, + 0x20000000000000000000000, + 0x40000000000000000000000, + 0x80000000000000000000000, + 0x100000000000000000000000, + 0x200000000000000000000000, + 0x400000000000000000000000, + 0x800000000000000000000000, + 0x1000000000000000000000000, + 0x2000000000000000000000000, + 0x4000000000000000000000000, + 0x8000000000000000000000000, + 0x10000000000000000000000000, + 0x20000000000000000000000000, + 0x40000000000000000000000000, + 0x80000000000000000000000000, + 0x100000000000000000000000000, + 0x200000000000000000000000000, + 0x400000000000000000000000000, + 0x800000000000000000000000000, + 0x1000000000000000000000000000, + 0x2000000000000000000000000000, + 0x4000000000000000000000000000, + 0x8000000000000000000000000000, + 0x10000000000000000000000000000, + 0x20000000000000000000000000000, + 0x40000000000000000000000000000, + 0x80000000000000000000000000000, + 0x100000000000000000000000000000, + 0x200000000000000000000000000000, + 0x400000000000000000000000000000, + 0x800000000000000000000000000000, + 0x1000000000000000000000000000000, + 0x2000000000000000000000000000000, + 0x4000000000000000000000000000000, + 0x8000000000000000000000000000000, + 0x10000000000000000000000000000000, + 0x20000000000000000000000000000000, + 0x40000000000000000000000000000000, + 0x80000000000000000000000000000000 + ]; + *hardcoded_results.span()[exponent] +} + +/// Calculate 2 raised to the power of the given exponent +/// using a pre-computed lookup table +/// # Arguments +/// * `exponent` - The exponent to raise 2 to +/// # Returns +/// * `felt252` - The result of 2^exponent +/// # Panics +/// * If `exponent` is greater than 251 (out of the supported range) +pub fn pow2_felt252(exponent: u32) -> felt252 { + let hardcoded_results: [felt252; 251] = [ + 0x1, + 0x2, + 0x4, + 0x8, + 0x10, + 0x20, + 0x40, + 0x80, + 0x100, + 0x200, + 0x400, + 0x800, + 0x1000, + 0x2000, + 0x4000, + 0x8000, + 0x10000, + 0x20000, + 0x40000, + 0x80000, + 0x100000, + 0x200000, + 0x400000, + 0x800000, + 0x1000000, + 0x2000000, + 0x4000000, + 0x8000000, + 0x10000000, + 0x20000000, + 0x40000000, + 0x80000000, + 0x100000000, + 0x200000000, + 0x400000000, + 0x800000000, + 0x1000000000, + 0x2000000000, + 0x4000000000, + 0x8000000000, + 0x10000000000, + 0x20000000000, + 0x40000000000, + 0x80000000000, + 0x100000000000, + 0x200000000000, + 0x400000000000, + 0x800000000000, + 0x1000000000000, + 0x2000000000000, + 0x4000000000000, + 0x8000000000000, + 0x10000000000000, + 0x20000000000000, + 0x40000000000000, + 0x80000000000000, + 0x100000000000000, + 0x200000000000000, + 0x400000000000000, + 0x800000000000000, + 0x1000000000000000, + 0x2000000000000000, + 0x4000000000000000, + 0x8000000000000000, + 0x10000000000000000, + 0x20000000000000000, + 0x40000000000000000, + 0x80000000000000000, + 0x100000000000000000, + 0x200000000000000000, + 0x400000000000000000, + 0x800000000000000000, + 0x1000000000000000000, + 0x2000000000000000000, + 0x4000000000000000000, + 0x8000000000000000000, + 0x10000000000000000000, + 0x20000000000000000000, + 0x40000000000000000000, + 0x80000000000000000000, + 0x100000000000000000000, + 0x200000000000000000000, + 0x400000000000000000000, + 0x800000000000000000000, + 0x1000000000000000000000, + 0x2000000000000000000000, + 0x4000000000000000000000, + 0x8000000000000000000000, + 0x10000000000000000000000, + 0x20000000000000000000000, + 0x40000000000000000000000, + 0x80000000000000000000000, + 0x100000000000000000000000, + 0x200000000000000000000000, + 0x400000000000000000000000, + 0x800000000000000000000000, + 0x1000000000000000000000000, + 0x2000000000000000000000000, + 0x4000000000000000000000000, + 0x8000000000000000000000000, + 0x10000000000000000000000000, + 0x20000000000000000000000000, + 0x40000000000000000000000000, + 0x80000000000000000000000000, + 0x100000000000000000000000000, + 0x200000000000000000000000000, + 0x400000000000000000000000000, + 0x800000000000000000000000000, + 0x1000000000000000000000000000, + 0x2000000000000000000000000000, + 0x4000000000000000000000000000, + 0x8000000000000000000000000000, + 0x10000000000000000000000000000, + 0x20000000000000000000000000000, + 0x40000000000000000000000000000, + 0x80000000000000000000000000000, + 0x100000000000000000000000000000, + 0x200000000000000000000000000000, + 0x400000000000000000000000000000, + 0x800000000000000000000000000000, + 0x1000000000000000000000000000000, + 0x2000000000000000000000000000000, + 0x4000000000000000000000000000000, + 0x8000000000000000000000000000000, + 0x10000000000000000000000000000000, + 0x20000000000000000000000000000000, + 0x40000000000000000000000000000000, + 0x80000000000000000000000000000000, + 0x100000000000000000000000000000000, + 0x200000000000000000000000000000000, + 0x400000000000000000000000000000000, + 0x800000000000000000000000000000000, + 0x1000000000000000000000000000000000, + 0x2000000000000000000000000000000000, + 0x4000000000000000000000000000000000, + 0x8000000000000000000000000000000000, + 0x10000000000000000000000000000000000, + 0x20000000000000000000000000000000000, + 0x40000000000000000000000000000000000, + 0x80000000000000000000000000000000000, + 0x100000000000000000000000000000000000, + 0x200000000000000000000000000000000000, + 0x400000000000000000000000000000000000, + 0x800000000000000000000000000000000000, + 0x1000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000, + 0x800000000000000000000000000000000000000, + 0x1000000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000000, + 0x800000000000000000000000000000000000000000, + 0x1000000000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000000000, + 0x800000000000000000000000000000000000000000000, + 0x1000000000000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000000000000, + 0x800000000000000000000000000000000000000000000000, + 0x1000000000000000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000000000000000, + 0x800000000000000000000000000000000000000000000000000, + 0x1000000000000000000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000000000000000000, + 0x800000000000000000000000000000000000000000000000000000, + 0x1000000000000000000000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000000000000000000000, + 0x800000000000000000000000000000000000000000000000000000000, + 0x1000000000000000000000000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000000000000000000000000, + 0x800000000000000000000000000000000000000000000000000000000000, + 0x1000000000000000000000000000000000000000000000000000000000000, + 0x2000000000000000000000000000000000000000000000000000000000000, + 0x4000000000000000000000000000000000000000000000000000000000000, + 0x8000000000000000000000000000000000000000000000000000000000000, + 0x10000000000000000000000000000000000000000000000000000000000000, + 0x20000000000000000000000000000000000000000000000000000000000000, + 0x40000000000000000000000000000000000000000000000000000000000000, + 0x80000000000000000000000000000000000000000000000000000000000000, + 0x100000000000000000000000000000000000000000000000000000000000000, + 0x200000000000000000000000000000000000000000000000000000000000000, + 0x400000000000000000000000000000000000000000000000000000000000000 + ]; + *hardcoded_results.span()[exponent] +} + +/// Calculate 10 raised to the power of the given exponent +/// using a pre-computed lookup table +/// # Arguments +/// * `exponent` - The exponent to raise 10 to +/// # Returns +/// * `u128` - The result of 10^exponent +/// # Panics +/// * If `exponent` is greater than 38 (out of the supported range) +pub fn pow10(exponent: u32) -> u128 { + let hardcoded_results: [u128; 38] = [ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000, + 100000000000000000000, + 1000000000000000000000, + 10000000000000000000000, + 100000000000000000000000, + 1000000000000000000000000, + 10000000000000000000000000, + 100000000000000000000000000, + 1000000000000000000000000000, + 10000000000000000000000000000, + 100000000000000000000000000000, + 1000000000000000000000000000000, + 10000000000000000000000000000000, + 100000000000000000000000000000000, + 1000000000000000000000000000000000, + 10000000000000000000000000000000000, + 100000000000000000000000000000000000, + 1000000000000000000000000000000000000, + 10000000000000000000000000000000000000 + ]; + *hardcoded_results.span()[exponent] +} + +/// Calculate 10 raised to the power of the given exponent +/// using a pre-computed lookup table +/// # Arguments +/// * `exponent` - The exponent to raise 10 to +/// # Returns +/// * `u128` - The result of 10^exponent +/// # Panics +/// * If `exponent` is greater than 77 (out of the supported range) +pub fn pow10_u256(exponent: u32) -> u256 { + let hardcoded_results: [u256; 77] = [ + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000, + 100000000000000000000, + 1000000000000000000000, + 10000000000000000000000, + 100000000000000000000000, + 1000000000000000000000000, + 10000000000000000000000000, + 100000000000000000000000000, + 1000000000000000000000000000, + 10000000000000000000000000000, + 100000000000000000000000000000, + 1000000000000000000000000000000, + 10000000000000000000000000000000, + 100000000000000000000000000000000, + 1000000000000000000000000000000000, + 10000000000000000000000000000000000, + 100000000000000000000000000000000000, + 1000000000000000000000000000000000000, + 10000000000000000000000000000000000000, + 100000000000000000000000000000000000000, + 1000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000000000000000000000000, + 100000000000000000000000000000000000000000000000000000000000000000000000000, + 1000000000000000000000000000000000000000000000000000000000000000000000000000, + 10000000000000000000000000000000000000000000000000000000000000000000000000000 + ]; + *hardcoded_results.span()[exponent] +} diff --git a/packages/math/src/karatsuba.cairo b/packages/math/src/karatsuba.cairo index a35a05c1..8c4a9c6e 100644 --- a/packages/math/src/karatsuba.cairo +++ b/packages/math/src/karatsuba.cairo @@ -1,6 +1,6 @@ //! # Karatsuba Multiplication. use core::cmp::max; -use super::{pow, count_digits_of_base}; +use super::{count_digits_of_base, const_pow::pow10}; /// Algorithm to multiply two numbers in O(n^1.6) running time /// # Arguments @@ -14,7 +14,9 @@ pub fn multiply(x: u128, y: u128) -> u128 { } let max_digit_counts = max(count_digits_of_base(x, 10), count_digits_of_base(y, 10)); + // the digits amount has to let middle_idx = div_half_ceil(max_digit_counts); + // free type conversion let (x1, x0) = split_number(x, middle_idx); let (y1, y0) = split_number(y, middle_idx); @@ -22,15 +24,15 @@ pub fn multiply(x: u128, y: u128) -> u128 { let z1 = multiply(x1, y1); let z2 = multiply(x0 + x1, y0 + y1); - return z0 + (z2 - z0 - z1) * pow(10, middle_idx) + z1 * pow(10, 2 * middle_idx); + return z0 + (z2 - z0 - z1) * pow10(middle_idx) + z1 * pow10(2 * middle_idx); } /// Helper function for 'multiply', divides an integer in half and rounds up strictly. /// # Arguments /// * `num` - The current value to be divided. /// # Returns -/// * `u128` - Half (rounded up) of num. -fn div_half_ceil(num: u128) -> u128 { +/// * `u32` - Half (rounded up) of num. +fn div_half_ceil(num: u32) -> u32 { if num % 2 != 0 { (num + 1) % 2 } else { @@ -44,7 +46,7 @@ fn div_half_ceil(num: u128) -> u128 { /// * `split_idx` - Index at which the number will be split /// # Returns /// * `(u128, u128)` -tuple representing the split number. -fn split_number(num: u128, split_idx: u128) -> (u128, u128) { - let divisor = pow(10, split_idx); +fn split_number(num: u128, split_idx: u32) -> (u128, u128) { + let divisor = pow10(split_idx); (num / divisor, num % divisor) } diff --git a/packages/math/src/lib.cairo b/packages/math/src/lib.cairo index 142f5c02..3f309bfb 100644 --- a/packages/math/src/lib.cairo +++ b/packages/math/src/lib.cairo @@ -3,6 +3,7 @@ pub mod armstrong_number; pub mod bip340; pub mod bitmap; pub mod collatz_sequence; +pub mod const_pow; pub mod ed25519; pub mod extended_euclidean_algorithm; pub mod fast_power; @@ -54,8 +55,8 @@ pub fn pow, +Mul, +Div, +Rem, +PartialEq, +Into, + /// * `num` - The number to count the digits of. /// * `base` - Base in which to count the digits. /// # Returns -/// * `felt252` - The number of digits in num of base -fn count_digits_of_base(mut num: u128, base: u128) -> u128 { +/// * `u32` - The number of digits in num of base +fn count_digits_of_base(mut num: u128, base: u128) -> u32 { let mut res = 0; while (num != 0) { num = num / base; diff --git a/packages/math/src/tests.cairo b/packages/math/src/tests.cairo index 97185775..0e3dbe51 100644 --- a/packages/math/src/tests.cairo +++ b/packages/math/src/tests.cairo @@ -3,6 +3,7 @@ mod armstrong_number_test; mod bip340_test; mod bitmap_test; mod collatz_sequence_test; +mod const_pow_test; mod ed25519_test; mod extended_euclidean_algorithm_test; mod fast_power_test; diff --git a/packages/math/src/tests/const_pow_test.cairo b/packages/math/src/tests/const_pow_test.cairo new file mode 100644 index 00000000..35284ba3 --- /dev/null +++ b/packages/math/src/tests/const_pow_test.cairo @@ -0,0 +1,92 @@ +use alexandria_math::const_pow::{pow2, pow2_u256, pow10, pow10_u256}; + +#[test] +#[available_gas(1000000000)] +fn pow2_test() { + assert_eq!(pow2(0), 1, "2^0 should be 1"); + assert_eq!(pow2(1), 2, "2^1 should be 2"); + assert_eq!(pow2(2), 4, "2^2 should be 4"); + assert_eq!(pow2(3), 8, "2^3 should be 8"); + assert_eq!(pow2(10), 1024, "2^10 should be 1024"); + assert_eq!(pow2(63), 0x8000000000000000, "2^63 should be 0x8000000000000000"); + assert_eq!( + pow2(127), + 0x80000000000000000000000000000000, + "2^127 should be 0x80000000000000000000000000000000" + ); +} + +#[test] +#[available_gas(1000000000)] +fn pow2_u256_test() { + assert_eq!(pow2_u256(0), 1, "2^0 should be 1"); + assert_eq!(pow2_u256(1), 2, "2^1 should be 2"); + assert_eq!( + pow2_u256(128), + 340282366920938463463374607431768211456, + "2^128 should be 340282366920938463463374607431768211456" + ); + assert_eq!( + pow2_u256(255), + 57896044618658097711785492504343953926634992332820282019728792003956564819968, + "2^255 should be 57896044618658097711785492504343953926634992332820282019728792003956564819968" + ); +} + +#[test] +#[available_gas(1000000000)] +fn pow10_test() { + assert_eq!(pow10(0), 1, "10^0 should be 1"); + assert_eq!(pow10(1), 10, "10^1 should be 10"); + assert_eq!(pow10(2), 100, "10^2 should be 100"); + assert_eq!(pow10(9), 1000000000, "10^9 should be 1000000000"); + assert_eq!(pow10(18), 1000000000000000000, "10^18 should be 1000000000000000000"); + assert_eq!( + pow10(37), + 10000000000000000000000000000000000000, + "10^37 should be 10000000000000000000000000000000000000" + ); +} + +#[test] +#[available_gas(1000000000)] +fn pow10_u256_test() { + assert_eq!(pow10_u256(0), 1, "10^0 should be 1"); + assert_eq!(pow10_u256(1), 10, "10^1 should be 10"); + assert_eq!(pow10_u256(2), 100, "10^2 should be 100"); + assert_eq!( + pow10_u256(38), + 100000000000000000000000000000000000000, + "10^38 should be 100000000000000000000000000000000000000" + ); + assert_eq!( + pow10_u256(76), + 10000000000000000000000000000000000000000000000000000000000000000000000000000, + "10^76 should be 10000000000000000000000000000000000000000000000000000000000000000000000000000" + ); +} + +#[test] +#[should_panic] +fn pow2_out_of_range_test() { + pow2(128); +} + +#[test] +#[should_panic] +fn pow2_u256_out_of_range_test() { + pow2_u256(256); +} + +#[test] +#[should_panic] +fn pow10_out_of_range_test() { + pow10(38); +} + +#[test] +#[should_panic] +fn pow10_u256_out_of_range_test() { + pow10_u256(77); +} + diff --git a/packages/merkle_tree/Scarb.toml b/packages/merkle_tree/Scarb.toml index 29fa0ab8..5e3f4b58 100644 --- a/packages/merkle_tree/Scarb.toml +++ b/packages/merkle_tree/Scarb.toml @@ -8,5 +8,8 @@ edition = "2023_11" [tool] fmt.workspace = true +[dependencies] +alexandria_math = { path = "../math" } + [dev-dependencies] -cairo_test.workspace = true \ No newline at end of file +cairo_test.workspace = true diff --git a/packages/merkle_tree/src/storage_proof.cairo b/packages/merkle_tree/src/storage_proof.cairo index a4623168..55cf79dc 100644 --- a/packages/merkle_tree/src/storage_proof.cairo +++ b/packages/merkle_tree/src/storage_proof.cairo @@ -1,6 +1,7 @@ use core::hash::HashStateTrait; use core::pedersen::PedersenTrait; use core::poseidon::PoseidonTrait; +use alexandria_math::const_pow::pow2_felt252; #[derive(Drop, Serde)] pub struct BinaryNode { @@ -131,7 +132,7 @@ fn traverse(expected_path: felt252, proof: Array) -> (felt252, felt252 let mut expected_hash = node_hash(@TrieNode::Edge(leaf)); let value = leaf.child; let mut path = leaf.path; - let mut path_length_pow2 = pow(2, leaf.length); + let mut path_length_pow2 = pow2_felt252(leaf.length.into()); loop { match nodes.pop_back() { @@ -149,7 +150,7 @@ fn traverse(expected_path: felt252, proof: Array) -> (felt252, felt252 TrieNode::Edge(edge_node) => { assert(expected_hash == *edge_node.child, 'invalid node hash'); path += *edge_node.path * path_length_pow2; - path_length_pow2 *= pow(2, *edge_node.length); + path_length_pow2 *= pow2_felt252((*edge_node.length).into()); } } expected_hash = node_hash(node); @@ -169,19 +170,6 @@ fn node_hash(node: @TrieNode) -> felt252 { } } -// TODO: replace with lookup table once array constants are available in Cairo -fn pow(x: felt252, n: u8) -> felt252 { - if n == 0 { - 1 - } else if n == 1 { - x - } else if (n & 1) == 1 { - x * pow(x * x, n / 2) - } else { - pow(x * x, n / 2) - } -} - #[inline(always)] fn pedersen_hash(a: felt252, b: felt252) -> felt252 { PedersenTrait::new(a).update(b).finalize()