diff --git a/src/math.cairo b/src/math.cairo index d448685..601a801 100644 --- a/src/math.cairo +++ b/src/math.cairo @@ -1,4 +1,5 @@ mod comp; mod core; mod hyp; +mod lut; mod trig; diff --git a/src/math/core.cairo b/src/math/core.cairo index 2af5064..1f82945 100644 --- a/src/math/core.cairo +++ b/src/math/core.cairo @@ -3,17 +3,9 @@ use result::{ResultTrait, ResultTraitImpl}; use traits::{Into, TryInto}; use integer::{u256_safe_divmod, u256_as_non_zero, upcast}; +use cubit::math::lut; use cubit::types::fixed::{ - HALF_u128, - MAX_u128, - ONE_u128, - Fixed, - FixedInto, - FixedTrait, - FixedAdd, - FixedDiv, - FixedMul, - FixedNeg + HALF_u128, MAX_u128, ONE_u128, Fixed, FixedInto, FixedTrait, FixedAdd, FixedDiv, FixedMul, FixedNeg }; // PUBLIC @@ -80,7 +72,7 @@ fn exp2(a: Fixed) -> Fixed { } let (int_part, frac_part) = _split_unsigned(a); - let int_res = FixedTrait::new_unscaled(_exp2(int_part), false); + let int_res = FixedTrait::new_unscaled(lut::exp2(int_part), false); let mut res_u = int_res; if frac_part > 0 { @@ -104,7 +96,7 @@ fn exp2(a: Fixed) -> Fixed { } fn exp2_int(exp: u128) -> Fixed { - return FixedTrait::new_unscaled(_exp2(exp), false); + return FixedTrait::new_unscaled(lut::exp2(exp), false); } fn floor(a: Fixed) -> Fixed { @@ -162,7 +154,7 @@ fn log2(a: Fixed) -> Fixed { return -log2(div); } - let (msb, div) = msb(a.mag); + let (msb, div) = lut::msb(a.mag / ONE_u128); let norm = a / FixedTrait::new_unscaled(div, false); let r8 = FixedTrait::new(167660832607149504, true) * norm; @@ -293,161 +285,6 @@ fn sub(a: Fixed, b: Fixed) -> Fixed { return add(a, -b); } -// Calculates the most significant bit -fn msb(a: u128) -> (u128, u128) { - let whole = a / ONE_u128; - - if whole < 256 { - if whole < 2 { return (0, 1); } - if whole < 4 { return (1, 2); } - if whole < 8 { return (2, 4); } - if whole < 16 { return (3, 8); } - if whole < 32 { return (4, 16); } - if whole < 64 { return (5, 32); } - if whole < 128 { return (6, 64); } - if whole < 256 { return (7, 128); } - } else if whole < 65536 { - if whole < 512 { return (8, 256); } - if whole < 1024 { return (9, 512); } - if whole < 2048 { return (10, 1024); } - if whole < 4096 { return (11, 2048); } - if whole < 8192 { return (12, 4096); } - if whole < 16384 { return (13, 8192); } - if whole < 32768 { return (14, 16384); } - if whole < 65536 { return (15, 32768); } - } else if whole < 16777216 { - if whole < 131072 { return (16, 65536); } - if whole < 262144 { return (17, 131072); } - if whole < 524288 { return (18, 262144); } - if whole < 1048576 { return (19, 524288); } - if whole < 2097152 { return (20, 1048576); } - if whole < 4194304 { return (21, 2097152); } - if whole < 8388608 { return (22, 4194304); } - if whole < 16777216 { return (23, 8388608); } - } else if whole < 4294967296 { - if whole < 33554432 { return (24, 16777216); } - if whole < 67108864 { return (25, 33554432); } - if whole < 134217728 { return (26, 67108864); } - if whole < 268435456 { return (27, 134217728); } - if whole < 536870912 { return (28, 268435456); } - if whole < 1073741824 { return (29, 536870912); } - if whole < 2147483648 { return (30, 1073741824); } - if whole < 4294967296 { return (31, 2147483648); } - } else if whole < 1099511627776 { - if whole < 8589934592 { return (32, 4294967296); } - if whole < 17179869184 { return (33, 8589934592); } - if whole < 34359738368 { return (34, 17179869184); } - if whole < 68719476736 { return (35, 34359738368); } - if whole < 137438953472 { return (36, 68719476736); } - if whole < 274877906944 { return (37, 137438953472); } - if whole < 549755813888 { return (38, 274877906944); } - if whole < 1099511627776 { return (39, 549755813888); } - } else if whole < 281474976710656 { - if whole < 2199023255552 { return (40, 1099511627776); } - if whole < 4398046511104 { return (41, 2199023255552); } - if whole < 8796093022208 { return (42, 4398046511104); } - if whole < 17592186044416 { return (43, 8796093022208); } - if whole < 35184372088832 { return (44, 17592186044416); } - if whole < 70368744177664 { return (45, 35184372088832); } - if whole < 140737488355328 { return (46, 70368744177664); } - if whole < 281474976710656 { return (47, 140737488355328); } - } else if whole < 72057594037927936 { - if whole < 562949953421312 { return (48, 281474976710656); } - if whole < 1125899906842624 { return (49, 562949953421312); } - if whole < 2251799813685248 { return (50, 1125899906842624); } - if whole < 4503599627370496 { return (51, 2251799813685248); } - if whole < 9007199254740992 { return (52, 4503599627370496); } - if whole < 18014398509481984 { return (53, 9007199254740992); } - if whole < 36028797018963968 { return (54, 18014398509481984); } - if whole < 72057594037927936 { return (55, 36028797018963968); } - } else { - if whole < 144115188075855872 { return (56, 72057594037927936); } - if whole < 288230376151711744 { return (57, 144115188075855872); } - if whole < 576460752303423488 { return (58, 288230376151711744); } - if whole < 1152921504606846976 { return (59, 576460752303423488); } - if whole < 2305843009213693952 { return (60, 1152921504606846976); } - if whole < 4611686018427387904 { return (61, 2305843009213693952); } - if whole < 9223372036854775808 { return (62, 4611686018427387904); } - if whole < 18446744073709551616 { return (63, 9223372036854775808); } - } - - return (64, 18446744073709551616); -} - -fn _exp2(exp: u128) -> u128 { - if exp <= 16 { - if exp == 0 { return 1; } - if exp == 1 { return 2; } - if exp == 2 { return 4; } - if exp == 3 { return 8; } - if exp == 4 { return 16; } - if exp == 5 { return 32; } - if exp == 6 { return 64; } - if exp == 7 { return 128; } - if exp == 8 { return 256; } - if exp == 9 { return 512; } - if exp == 10 { return 1024; } - if exp == 11 { return 2048; } - if exp == 12 { return 4096; } - if exp == 13 { return 8192; } - if exp == 14 { return 16384; } - if exp == 15 { return 32768; } - if exp == 16 { return 65536; } - } else if exp <= 32 { - if exp == 17 { return 131072; } - if exp == 18 { return 262144; } - if exp == 19 { return 524288; } - if exp == 20 { return 1048576; } - if exp == 21 { return 2097152; } - if exp == 22 { return 4194304; } - if exp == 23 { return 8388608; } - if exp == 24 { return 16777216; } - if exp == 25 { return 33554432; } - if exp == 26 { return 67108864; } - if exp == 27 { return 134217728; } - if exp == 28 { return 268435456; } - if exp == 29 { return 536870912; } - if exp == 30 { return 1073741824; } - if exp == 31 { return 2147483648; } - if exp == 32 { return 4294967296; } - } else if exp <= 48 { - if exp == 33 { return 8589934592; } - if exp == 34 { return 17179869184; } - if exp == 35 { return 34359738368; } - if exp == 36 { return 68719476736; } - if exp == 37 { return 137438953472; } - if exp == 38 { return 274877906944; } - if exp == 39 { return 549755813888; } - if exp == 40 { return 1099511627776; } - if exp == 41 { return 2199023255552; } - if exp == 42 { return 4398046511104; } - if exp == 43 { return 8796093022208; } - if exp == 44 { return 17592186044416; } - if exp == 45 { return 35184372088832; } - if exp == 46 { return 70368744177664; } - if exp == 47 { return 140737488355328; } - if exp == 48 { return 281474976710656; } - } else { - if exp == 49 { return 562949953421312; } - if exp == 50 { return 1125899906842624; } - if exp == 51 { return 2251799813685248; } - if exp == 52 { return 4503599627370496; } - if exp == 53 { return 9007199254740992; } - if exp == 54 { return 18014398509481984; } - if exp == 55 { return 36028797018963968; } - if exp == 56 { return 72057594037927936; } - if exp == 57 { return 144115188075855872; } - if exp == 58 { return 288230376151711744; } - if exp == 59 { return 576460752303423488; } - if exp == 60 { return 1152921504606846976; } - if exp == 61 { return 2305843009213693952; } - if exp == 62 { return 4611686018427387904; } - if exp == 63 { return 9223372036854775808; } - } - - return 18446744073709551616; -} - // Ignores sign and always returns false fn _split_unsigned(a: Fixed) -> (u128, u128) { return integer::u128_safe_divmod(a.mag, integer::u128_as_non_zero(ONE_u128)); @@ -598,7 +435,7 @@ fn test_sqrt() { #[available_gas(100000)] fn test_msb() { let a = FixedTrait::new_unscaled(4503599627370495, false); - let (msb, div) = msb(a.mag); + let (msb, div) = lut::msb(a.mag / ONE_u128); assert(msb == 51, 'invalid msb'); assert(div == 2251799813685248, 'invalid msb ceil'); } diff --git a/src/math/lut.cairo b/src/math/lut.cairo new file mode 100644 index 0000000..9ade1d4 --- /dev/null +++ b/src/math/lut.cairo @@ -0,0 +1,586 @@ +// Calculates the most significant bit +fn msb(whole: u128) -> (u128, u128) { + if whole < 256 { + if whole < 2 { return (0, 1); } + if whole < 4 { return (1, 2); } + if whole < 8 { return (2, 4); } + if whole < 16 { return (3, 8); } + if whole < 32 { return (4, 16); } + if whole < 64 { return (5, 32); } + if whole < 128 { return (6, 64); } + if whole < 256 { return (7, 128); } + } else if whole < 65536 { + if whole < 512 { return (8, 256); } + if whole < 1024 { return (9, 512); } + if whole < 2048 { return (10, 1024); } + if whole < 4096 { return (11, 2048); } + if whole < 8192 { return (12, 4096); } + if whole < 16384 { return (13, 8192); } + if whole < 32768 { return (14, 16384); } + if whole < 65536 { return (15, 32768); } + } else if whole < 16777216 { + if whole < 131072 { return (16, 65536); } + if whole < 262144 { return (17, 131072); } + if whole < 524288 { return (18, 262144); } + if whole < 1048576 { return (19, 524288); } + if whole < 2097152 { return (20, 1048576); } + if whole < 4194304 { return (21, 2097152); } + if whole < 8388608 { return (22, 4194304); } + if whole < 16777216 { return (23, 8388608); } + } else if whole < 4294967296 { + if whole < 33554432 { return (24, 16777216); } + if whole < 67108864 { return (25, 33554432); } + if whole < 134217728 { return (26, 67108864); } + if whole < 268435456 { return (27, 134217728); } + if whole < 536870912 { return (28, 268435456); } + if whole < 1073741824 { return (29, 536870912); } + if whole < 2147483648 { return (30, 1073741824); } + if whole < 4294967296 { return (31, 2147483648); } + } else if whole < 1099511627776 { + if whole < 8589934592 { return (32, 4294967296); } + if whole < 17179869184 { return (33, 8589934592); } + if whole < 34359738368 { return (34, 17179869184); } + if whole < 68719476736 { return (35, 34359738368); } + if whole < 137438953472 { return (36, 68719476736); } + if whole < 274877906944 { return (37, 137438953472); } + if whole < 549755813888 { return (38, 274877906944); } + if whole < 1099511627776 { return (39, 549755813888); } + } else if whole < 281474976710656 { + if whole < 2199023255552 { return (40, 1099511627776); } + if whole < 4398046511104 { return (41, 2199023255552); } + if whole < 8796093022208 { return (42, 4398046511104); } + if whole < 17592186044416 { return (43, 8796093022208); } + if whole < 35184372088832 { return (44, 17592186044416); } + if whole < 70368744177664 { return (45, 35184372088832); } + if whole < 140737488355328 { return (46, 70368744177664); } + if whole < 281474976710656 { return (47, 140737488355328); } + } else if whole < 72057594037927936 { + if whole < 562949953421312 { return (48, 281474976710656); } + if whole < 1125899906842624 { return (49, 562949953421312); } + if whole < 2251799813685248 { return (50, 1125899906842624); } + if whole < 4503599627370496 { return (51, 2251799813685248); } + if whole < 9007199254740992 { return (52, 4503599627370496); } + if whole < 18014398509481984 { return (53, 9007199254740992); } + if whole < 36028797018963968 { return (54, 18014398509481984); } + if whole < 72057594037927936 { return (55, 36028797018963968); } + } else { + if whole < 144115188075855872 { return (56, 72057594037927936); } + if whole < 288230376151711744 { return (57, 144115188075855872); } + if whole < 576460752303423488 { return (58, 288230376151711744); } + if whole < 1152921504606846976 { return (59, 576460752303423488); } + if whole < 2305843009213693952 { return (60, 1152921504606846976); } + if whole < 4611686018427387904 { return (61, 2305843009213693952); } + if whole < 9223372036854775808 { return (62, 4611686018427387904); } + if whole < 18446744073709551616 { return (63, 9223372036854775808); } + } + + return (64, 18446744073709551616); +} + +fn exp2(exp: u128) -> u128 { + if exp <= 16 { + if exp == 0 { return 1; } + if exp == 1 { return 2; } + if exp == 2 { return 4; } + if exp == 3 { return 8; } + if exp == 4 { return 16; } + if exp == 5 { return 32; } + if exp == 6 { return 64; } + if exp == 7 { return 128; } + if exp == 8 { return 256; } + if exp == 9 { return 512; } + if exp == 10 { return 1024; } + if exp == 11 { return 2048; } + if exp == 12 { return 4096; } + if exp == 13 { return 8192; } + if exp == 14 { return 16384; } + if exp == 15 { return 32768; } + if exp == 16 { return 65536; } + } else if exp <= 32 { + if exp == 17 { return 131072; } + if exp == 18 { return 262144; } + if exp == 19 { return 524288; } + if exp == 20 { return 1048576; } + if exp == 21 { return 2097152; } + if exp == 22 { return 4194304; } + if exp == 23 { return 8388608; } + if exp == 24 { return 16777216; } + if exp == 25 { return 33554432; } + if exp == 26 { return 67108864; } + if exp == 27 { return 134217728; } + if exp == 28 { return 268435456; } + if exp == 29 { return 536870912; } + if exp == 30 { return 1073741824; } + if exp == 31 { return 2147483648; } + if exp == 32 { return 4294967296; } + } else if exp <= 48 { + if exp == 33 { return 8589934592; } + if exp == 34 { return 17179869184; } + if exp == 35 { return 34359738368; } + if exp == 36 { return 68719476736; } + if exp == 37 { return 137438953472; } + if exp == 38 { return 274877906944; } + if exp == 39 { return 549755813888; } + if exp == 40 { return 1099511627776; } + if exp == 41 { return 2199023255552; } + if exp == 42 { return 4398046511104; } + if exp == 43 { return 8796093022208; } + if exp == 44 { return 17592186044416; } + if exp == 45 { return 35184372088832; } + if exp == 46 { return 70368744177664; } + if exp == 47 { return 140737488355328; } + if exp == 48 { return 281474976710656; } + } else { + if exp == 49 { return 562949953421312; } + if exp == 50 { return 1125899906842624; } + if exp == 51 { return 2251799813685248; } + if exp == 52 { return 4503599627370496; } + if exp == 53 { return 9007199254740992; } + if exp == 54 { return 18014398509481984; } + if exp == 55 { return 36028797018963968; } + if exp == 56 { return 72057594037927936; } + if exp == 57 { return 144115188075855872; } + if exp == 58 { return 288230376151711744; } + if exp == 59 { return 576460752303423488; } + if exp == 60 { return 1152921504606846976; } + if exp == 61 { return 2305843009213693952; } + if exp == 62 { return 4611686018427387904; } + if exp == 63 { return 9223372036854775808; } + } + + return 18446744073709551616; +} + +fn sin(a: u128) -> (u128, u128, u128) { + let slot = a / 113187804032455040; + + if slot < 128 { + if slot < 64 { + if slot < 32 { + if slot < 16 { +if slot == 0 { return (0, 0, 113187093788755728); } +if slot == 1 { return (113187804032455040, 113187093788755728, 226369926147403904); } +if slot == 2 { return (226375608064910080, 226369926147403904, 339544235806277440); } +if slot == 3 { return (339563412097365120, 339544235806277440, 452705761816584064); } +if slot == 4 { return (452751216129820160, 452705761816584064, 565850243710828608); } +if slot == 5 { return (565939020162275200, 565850243710828608, 678973421663217280); } +if slot == 6 { return (679126824194730240, 678973421663217280, 792071036650037632); } +if slot == 7 { return (792314628227185280, 792071036650037632, 905138830610008576); } +if slot == 8 { return (905502432259640320, 905138830610008576, 1018172546604593536); } +if slot == 9 { return (1018690236292095360, 1018172546604593536, 1131167928978272256); } +if slot == 10 { return (1131878040324550400, 1131167928978272256, 1244120723518763520); } +if slot == 11 { return (1245065844357005312, 1244120723518763520, 1357026677617194496); } +if slot == 12 { return (1358253648389460480, 1357026677617194496, 1469881540428207616); } +if slot == 13 { return (1471441452421915648, 1469881540428207616, 1582681063030003968); } +if slot == 14 { return (1584629256454370560, 1582681063030003968, 1695420998584312064); } +if slot == 15 { return (1697817060486825472, 1695420998584312064, 1808097102496279552); } + } else { +if slot == 16 { return (1811004864519280640, 1808097102496279552, 1920705132574278656); } +if slot == 17 { return (1924192668551735808, 1920705132574278656, 2033240849189623040); } +if slot == 18 { return (2037380472584190720, 2033240849189623040, 2145700015436187648); } +if slot == 19 { return (2150568276616645632, 2145700015436187648, 2258078397289926144); } +if slot == 20 { return (2263756080649100800, 2258078397289926144, 2370371763768277504); } +if slot == 21 { return (2376943884681555968, 2370371763768277504, 2482575887089462272); } +if slot == 22 { return (2490131688714010624, 2482575887089462272, 2594686542831655936); } +if slot == 23 { return (2603319492746465792, 2594686542831655936, 2706699510092035072); } +if slot == 24 { return (2716507296778920960, 2706699510092035072, 2818610571645691904); } +if slot == 25 { return (2829695100811376128, 2818610571645691904, 2930415514104411136); } +if slot == 26 { return (2942882904843831296, 2930415514104411136, 3042110128075301888); } +if slot == 27 { return (3056070708876285952, 3042110128075301888, 3153690208319278080); } +if slot == 28 { return (3169258512908741120, 3153690208319278080, 3265151553909384192); } +if slot == 29 { return (3282446316941196288, 3265151553909384192, 3376489968388956672); } +if slot == 30 { return (3395634120973650944, 3376489968388956672, 3487701259929618944); } +if slot == 31 { return (3508821925006106112, 3487701259929618944, 3598781241489101824); } + } + } else { + if slot < 48 { +if slot == 32 { return (3622009729038561280, 3598781241489101824, 3709725730968882176); } +if slot == 33 { return (3735197533071016448, 3709725730968882176, 3820530551371637248); } +if slot == 34 { return (3848385337103471616, 3820530551371637248, 3931191530958504960); } +if slot == 35 { return (3961573141135926272, 3931191530958504960, 4041704503406151168); } +if slot == 36 { return (4074760945168381440, 4041704503406151168, 4152065307963624448); } +if slot == 37 { return (4187948749200836608, 4152065307963624448, 4262269789609009664); } +if slot == 38 { return (4301136553233291264, 4262269789609009664, 4372313799205860864); } +if slot == 39 { return (4414324357265746432, 4372313799205860864, 4482193193659413504); } +if slot == 40 { return (4527512161298201600, 4482193193659413504, 4591903836072570880); } +if slot == 41 { return (4640699965330656256, 4591903836072570880, 4701441595901655040); } +if slot == 42 { return (4753887769363111936, 4701441595901655040, 4810802349111918592); } +if slot == 43 { return (4867075573395566592, 4810802349111918592, 4919981978332814336); } +if slot == 44 { return (4980263377428021248, 4919981978332814336, 5028976373013011456); } +if slot == 45 { return (5093451181460476928, 5028976373013011456, 5137781429575152640); } +if slot == 46 { return (5206638985492931584, 5137781429575152640, 5246393051570358272); } +if slot == 47 { return (5319826789525387264, 5246393051570358272, 5354807149832442880); } + } else { +if slot == 48 { return (5433014593557841920, 5354807149832442880, 5463019642631886848); } +if slot == 49 { return (5546202397590296576, 5463019642631886848, 5571026455829497856); } +if slot == 50 { return (5659390201622752256, 5571026455829497856, 5678823523029801984); } +if slot == 51 { return (5772578005655206912, 5678823523029801984, 5786406785734150144); } +if slot == 52 { return (5885765809687662592, 5786406785734150144, 5893772193493504000); } +if slot == 53 { return (5998953613720117248, 5893772193493504000, 6000915704060946432); } +if slot == 54 { return (6112141417752571904, 6000915704060946432, 6107833283543863296); } +if slot == 55 { return (6225329221785027584, 6107833283543863296, 6214520906555814912); } +if slot == 56 { return (6338517025817482240, 6214520906555814912, 6320974556368096256); } +if slot == 57 { return (6451704829849936896, 6320974556368096256, 6427190225060960256); } +if slot == 58 { return (6564892633882392576, 6427190225060960256, 6533163913674512384); } +if slot == 59 { return (6678080437914847232, 6533163913674512384, 6638891632359274496); } +if slot == 60 { return (6791268241947301888, 6638891632359274496, 6744369400526395392); } +if slot == 61 { return (6904456045979757568, 6744369400526395392, 6849593246997518336); } +if slot == 62 { return (7017643850012212224, 6849593246997518336, 6954559210154299392); } +if slot == 63 { return (7130831654044667904, 6954559210154299392, 7059263338087549952); } + } + } + } else { + if slot < 96 { + if slot < 80 { +if slot == 64 { return (7244019458077122560, 7059263338087549952, 7163701688746032128); } +if slot == 65 { return (7357207262109577216, 7163701688746032128, 7267870330084875264); } +if slot == 66 { return (7470395066142032896, 7267870330084875264, 7371765340213606400); } +if slot == 67 { return (7583582870174487552, 7371765340213606400, 7475382807543818240); } +if slot == 68 { return (7696770674206943232, 7475382807543818240, 7578718830936428544); } +if slot == 69 { return (7809958478239397888, 7578718830936428544, 7681769519848563712); } +if slot == 70 { return (7923146282271852544, 7681769519848563712, 7784530994480030720); } +if slot == 71 { return (8036334086304308224, 7784530994480030720, 7886999385919389696); } +if slot == 72 { return (8149521890336762880, 7886999385919389696, 7989170836289618944); } +if slot == 73 { return (8262709694369217536, 7989170836289618944, 8091041498893360128); } +if slot == 74 { return (8375897498401673216, 8091041498893360128, 8192607538357741568); } +if slot == 75 { return (8489085302434127872, 8192607538357741568, 8293865130778784768); } +if slot == 76 { return (8602273106466582528, 8293865130778784768, 8394810463865367552); } +if slot == 77 { return (8715460910499038208, 8394810463865367552, 8495439737082749952); } +if slot == 78 { return (8828648714531492864, 8495439737082749952, 8595749161795674112); } +if slot == 79 { return (8941836518563948544, 8595749161795674112, 8695734961410993152); } + } else { +if slot == 80 { return (9055024322596403200, 8695734961410993152, 8795393371519863808); } +if slot == 81 { return (9168212126628857856, 8795393371519863808, 8894720640039474176); } +if slot == 82 { return (9281399930661312512, 8894720640039474176, 8993713027354304512); } +if slot == 83 { return (9394587734693769216, 8993713027354304512, 9092366806456920064); } +if slot == 84 { return (9507775538726223872, 9092366806456920064, 9190678263088299008); } +if slot == 85 { return (9620963342758678528, 9190678263088299008, 9288643695877666816); } +if slot == 86 { return (9734151146791133184, 9288643695877666816, 9386259416481845248); } +if slot == 87 { return (9847338950823587840, 9386259416481845248, 9483521749724129280); } +if slot == 88 { return (9960526754856042496, 9483521749724129280, 9580427033732646912); } +if slot == 89 { return (10073714558888499200, 9580427033732646912, 9676971620078221312); } +if slot == 90 { return (10186902362920953856, 9676971620078221312, 9773151873911744512); } +if slot == 91 { return (10300090166953408512, 9773151873911744512, 9868964174101024768); } +if slot == 92 { return (10413277970985863168, 9868964174101024768, 9964404913367111680); } +if slot == 93 { return (10526465775018317824, 9964404913367111680, 10059470498420115456); } +if slot == 94 { return (10639653579050774528, 10059470498420115456, 10154157350094487552); } +if slot == 95 { return (10752841383083229184, 10154157350094487552, 10248461903483781120); } + } + } else { + if slot < 112 { +if slot == 96 { return (10866029187115683840, 10248461903483781120, 10342380608074864640); } +if slot == 97 { return (10979216991148138496, 10342380608074864640, 10435909927881588736); } +if slot == 98 { return (11092404795180593152, 10435909927881588736, 10529046341577930752); } +if slot == 99 { return (11205592599213049856, 10529046341577930752, 10621786342630553600); } +if slot == 100 { return (11318780403245504512, 10621786342630553600, 10714126439430832128); } +if slot == 101 { return (11431968207277959168, 10714126439430832128, 10806063155426316288); } +if slot == 102 { return (11545156011310413824, 10806063155426316288, 10897593029251608576); } +if slot == 103 { return (11658343815342868480, 10897593029251608576, 10988712614858696704); } +if slot == 104 { return (11771531619375325184, 10988712614858696704, 11079418481646680064); } +if slot == 105 { return (11884719423407779840, 11079418481646680064, 11169707214590941184); } +if slot == 106 { return (11997907227440234496, 11169707214590941184, 11259575414371717120); } +if slot == 107 { return (12111095031472689152, 11259575414371717120, 11349019697502078976); } +if slot == 108 { return (12224282835505143808, 11349019697502078976, 11438036696455321600); } +if slot == 109 { return (12337470639537598464, 11438036696455321600, 11526623059791749120); } +if slot == 110 { return (12450658443570055168, 11526623059791749120, 11614775452284848128); } +if slot == 111 { return (12563846247602509824, 11614775452284848128, 11702490555046866944); } + } else { +if slot == 112 { return (12677034051634964480, 11702490555046866944, 11789765065653762048); } +if slot == 113 { return (12790221855667419136, 11789765065653762048, 11876595698269536256); } +if slot == 114 { return (12903409659699873792, 11876595698269536256, 11962979183769952256); } +if slot == 115 { return (13016597463732330496, 11962979183769952256, 12048912269865601024); } +if slot == 116 { return (13129785267764785152, 12048912269865601024, 12134391721224361984); } +if slot == 117 { return (13242973071797239808, 12134391721224361984, 12219414319593205760); } +if slot == 118 { return (13356160875829694464, 12219414319593205760, 12303976863919355904); } +if slot == 119 { return (13469348679862149120, 12303976863919355904, 12388076170470815744); } +if slot == 120 { return (13582536483894603776, 12388076170470815744, 12471709072956227584); } +if slot == 121 { return (13695724287927060480, 12471709072956227584, 12554872422644080640); } +if slot == 122 { return (13808912091959515136, 12554872422644080640, 12637563088481263616); } +if slot == 123 { return (13922099895991969792, 12637563088481263616, 12719777957210943488); } +if slot == 124 { return (14035287700024424448, 12719777957210943488, 12801513933489782784); } +if slot == 125 { return (14148475504056879104, 12801513933489782784, 12882767940004474880); } +if slot == 126 { return (14261663308089335808, 12882767940004474880, 12963536917587591168); } +if slot == 127 { return (14374851112121790464, 12963536917587591168, 13043817825332781056); } + } + } + } + } else { + if slot < 192 { + if slot < 160 { + if slot < 144 { +if slot == 128 { return (14488038916154245120, 13043817825332781056, 13123607640709244928); } +if slot == 129 { return (14601226720186699776, 13123607640709244928, 13202903359675527168); } +if slot == 130 { return (14714414524219154432, 13202903359675527168, 13281701996792631296); } +if slot == 131 { return (14827602328251611136, 13281701996792631296, 13360000585336401920); } +if slot == 132 { return (14940790132284065792, 13360000585336401920, 13437796177409239040); } +if slot == 133 { return (15053977936316520448, 13437796177409239040, 13515085844051068928); } +if slot == 134 { return (15167165740348975104, 13515085844051068928, 13591866675349626880); } +if slot == 135 { return (15280353544381429760, 13591866675349626880, 13668135780550014976); } +if slot == 136 { return (15393541348413886464, 13668135780550014976, 13743890288163524608); } +if slot == 137 { return (15506729152446341120, 13743890288163524608, 13819127346075762688); } +if slot == 138 { return (15619916956478795776, 13819127346075762688, 13893844121654026240); } +if slot == 139 { return (15733104760511250432, 13893844121654026240, 13968037801853939712); } +if slot == 140 { return (15846292564543705088, 13968037801853939712, 14041705593325379584); } +if slot == 141 { return (15959480368576159744, 14041705593325379584, 14114844722517637120); } +if slot == 142 { return (16072668172608616448, 14114844722517637120, 14187452435783831552); } +if slot == 143 { return (16185855976641071104, 14187452435783831552, 14259525999484600320); } + } else { +if slot == 144 { return (16299043780673525760, 14259525999484600320, 14331062700091006976); } +if slot == 145 { return (16412231584705980416, 14331062700091006976, 14402059844286701568); } +if slot == 146 { return (16525419388738435072, 14402059844286701568, 14472514759069339648); } +if slot == 147 { return (16638607192770891776, 14472514759069339648, 14542424791851194368); } +if slot == 148 { return (16751794996803346432, 14542424791851194368, 14611787310559047680); } +if slot == 149 { return (16864982800835801088, 14611787310559047680, 14680599703733276672); } +if slot == 150 { return (16978170604868255744, 14680599703733276672, 14748859380626165760); } +if slot == 151 { return (17091358408900710400, 14748859380626165760, 14816563771299463168); } +if slot == 152 { return (17204546212933165056, 14816563771299463168, 14883710326721128448); } +if slot == 153 { return (17317734016965621760, 14883710326721128448, 14950296518861299712); } +if slot == 154 { return (17430921820998076416, 14950296518861299712, 15016319840787476480); } +if slot == 155 { return (17544109625030531072, 15016319840787476480, 15081777806758907904); } +if slot == 156 { return (17657297429062985728, 15081777806758907904, 15146667952320167936); } +if slot == 157 { return (17770485233095440384, 15146667952320167936, 15210987834393956352); } +if slot == 158 { return (17883673037127897088, 15210987834393956352, 15274735031373064192); } +if slot == 159 { return (17996860841160351744, 15274735031373064192, 15337907143211554816); } +if slot == 160 { return (18110048645192806400, 15337907143211554816, 15400501791515123712); } + } + } else { + if slot < 176 { +if slot == 161 { return (18223236449225261056, 15400501791515123712, 15462516619630641152); } +if slot == 162 { return (18336424253257715712, 15462516619630641152, 15523949292734879744); } +if slot == 163 { return (18449612057290170368, 15523949292734879744, 15584797497922418688); } +if slot == 164 { return (18562799861322625024, 15584797497922418688, 15645058944292722688); } +if slot == 165 { return (18675987665355079680, 15645058944292722688, 15704731363036401664); } +if slot == 166 { return (18789175469387538432, 15704731363036401664, 15763812507520608256); } +if slot == 167 { return (18902363273419993088, 15763812507520608256, 15822300153373650944); } +if slot == 168 { return (19015551077452447744, 15822300153373650944, 15880192098568718336); } +if slot == 169 { return (19128738881484902400, 15880192098568718336, 15937486163506794496); } +if slot == 170 { return (19241926685517357056, 15937486163506794496, 15994180191098720256); } +if slot == 171 { return (19355114489549811712, 15994180191098720256, 16050272046846396416); } +if slot == 172 { return (19468302293582266368, 16050272046846396416, 16105759618923161600); } +if slot == 173 { return (19581490097614721024, 16105759618923161600, 16160640818253285376); } +if slot == 174 { return (19694677901647175680, 16160640818253285376, 16214913578590631936); } +if slot == 175 { return (19807865705679630336, 16214913578590631936, 16268575856596451328); } + } else { +if slot == 176 { return (19921053509712084992, 16268575856596451328, 16321625631916308480); } +if slot == 177 { return (20034241313744543744, 16321625631916308480, 16374060907256141824); } +if slot == 178 { return (20147429117776998400, 16374060907256141824, 16425879708457474048); } +if slot == 179 { return (20260616921809453056, 16425879708457474048, 16477080084571727872); } +if slot == 180 { return (20373804725841907712, 16477080084571727872, 16527660107933679616); } +if slot == 181 { return (20486992529874362368, 16527660107933679616, 16577617874234040320); } +if slot == 182 { return (20600180333906817024, 16577617874234040320, 16626951502591143936); } +if slot == 183 { return (20713368137939271680, 16626951502591143936, 16675659135621765120); } +if slot == 184 { return (20826555941971726336, 16675659135621765120, 16723738939511050240); } +if slot == 185 { return (20939743746004180992, 16723738939511050240, 16771189104081559552); } +if slot == 186 { return (21052931550036635648, 16771189104081559552, 16818007842861414400); } +if slot == 187 { return (21166119354069094400, 16818007842861414400, 16864193393151561728); } +if slot == 188 { return (21279307158101549056, 16864193393151561728, 16909744016092137472); } +if slot == 189 { return (21392494962134003712, 16909744016092137472, 16954657996727932928); } +if slot == 190 { return (21505682766166458368, 16954657996727932928, 16998933644072962048); } +if slot == 191 { return (21618870570198913024, 16998933644072962048, 17042569291174129664); } + } + } + } else { + if slot < 224 { + if slot < 208 { +if slot == 192 { return (21732058374231367680, 17042569291174129664, 17085563295173984256); } +if slot == 193 { return (21845246178263822336, 17085563295173984256, 17127914037372573696); } +if slot == 194 { return (21958433982296276992, 17127914037372573696, 17169619923288391680); } +if slot == 195 { return (22071621786328731648, 17169619923288391680, 17210679382718406656); } +if slot == 196 { return (22184809590361186304, 17210679382718406656, 17251090869797177344); } +if slot == 197 { return (22297997394393640960, 17251090869797177344, 17290852863055058944); } +if slot == 198 { return (22411185198426099712, 17290852863055058944, 17329963865475473408); } +if slot == 199 { return (22524373002458554368, 17329963865475473408, 17368422404551288832); } +if slot == 200 { return (22637560806491009024, 17368422404551288832, 17406227032340244480); } +if slot == 201 { return (22750748610523463680, 17406227032340244480, 17443376325519470592); } +if slot == 202 { return (22863936414555918336, 17443376325519470592, 17479868885439078400); } +if slot == 203 { return (22977124218588372992, 17479868885439078400, 17515703338174812160); } +if slot == 204 { return (23090312022620827648, 17515703338174812160, 17550878334579777536); } +if slot == 205 { return (23203499826653282304, 17550878334579777536, 17585392550335242240); } +if slot == 206 { return (23316687630685736960, 17585392550335242240, 17619244686000492544); } +if slot == 207 { return (23429875434718191616, 17619244686000492544, 17652433467061753856); } + } else { +if slot == 208 { return (23543063238750650368, 17652433467061753856, 17684957643980173312); } +if slot == 209 { return (23656251042783105024, 17684957643980173312, 17716815992238874624); } +if slot == 210 { return (23769438846815559680, 17716815992238874624, 17748007312389054464); } +if slot == 211 { return (23882626650848014336, 17748007312389054464, 17778530430095136768); } +if slot == 212 { return (23995814454880468992, 17778530430095136768, 17808384196178993152); } +if slot == 213 { return (24109002258912923648, 17808384196178993152, 17837567486663200768); } +if slot == 214 { return (24222190062945378304, 17837567486663200768, 17866079202813370368); } +if slot == 215 { return (24335377866977832960, 17866079202813370368, 17893918271179503616); } +if slot == 216 { return (24448565671010287616, 17893918271179503616, 17921083643636410368); } +if slot == 217 { return (24561753475042742272, 17921083643636410368, 17947574297423169536); } +if slot == 218 { return (24674941279075196928, 17947574297423169536, 17973389235181643776); } +if slot == 219 { return (24788129083107655680, 17973389235181643776, 17998527484994013184); } +if slot == 220 { return (24901316887140110336, 17998527484994013184, 18022988100419381248); } +if slot == 221 { return (25014504691172564992, 18022988100419381248, 18046770160529401856); } +if slot == 222 { return (25127692495205019648, 18046770160529401856, 18069872769942953984); } +if slot == 223 { return (25240880299237474304, 18069872769942953984, 18092295058859851776); } + } + } else { + if slot < 240 { +if slot == 224 { return (25354068103269928960, 18092295058859851776, 18114036183093590016); } +if slot == 225 { return (25467255907302383616, 18114036183093590016, 18135095324103129088); } +if slot == 226 { return (25580443711334838272, 18135095324103129088, 18155471689023713280); } +if slot == 227 { return (25693631515367292928, 18155471689023713280, 18175164510696720384); } +if slot == 228 { return (25806819319399747584, 18175164510696720384, 18194173047698544640); } +if slot == 229 { return (25920007123432202240, 18194173047698544640, 18212496584368510976); } +if slot == 230 { return (26033194927464660992, 18212496584368510976, 18230134430835818496); } +if slot == 231 { return (26146382731497115648, 18230134430835818496, 18247085923045521408); } +if slot == 232 { return (26259570535529570304, 18247085923045521408, 18263350422783514624); } +if slot == 233 { return (26372758339562024960, 18263350422783514624, 18278927317700577280); } +if slot == 234 { return (26485946143594479616, 18278927317700577280, 18293816021335420928); } +if slot == 235 { return (26599133947626934272, 18293816021335420928, 18308015973136766976); } +if slot == 236 { return (26712321751659388928, 18308015973136766976, 18321526638484457472); } +if slot == 237 { return (26825509555691843584, 18321526638484457472, 18334347508709578752); } +if slot == 238 { return (26938697359724298240, 18334347508709578752, 18346478101113610240); } +if slot == 239 { return (27051885163756752896, 18346478101113610240, 18357917958986606592); } + } else { +if slot == 240 { return (27165072967789207552, 18357917958986606592, 18368666651624386560); } +if slot == 241 { return (27278260771821666304, 18368666651624386560, 18378723774344742912); } +if slot == 242 { return (27391448575854120960, 18378723774344742912, 18388088948502691840); } +if slot == 243 { return (27504636379886575616, 18388088948502691840, 18396761821504720896); } +if slot == 244 { return (27617824183919030272, 18396761821504720896, 18404742066822062080); } +if slot == 245 { return (27731011987951484928, 18404742066822062080, 18412029384002988032); } +if slot == 246 { return (27844199791983939584, 18412029384002988032, 18418623498684129280); } +if slot == 247 { return (27957387596016394240, 18418623498684129280, 18424524162600792064); } +if slot == 248 { return (28070575400048848896, 18424524162600792064, 18429731153596315648); } +if slot == 249 { return (28183763204081303552, 18429731153596315648, 18434244275630434304); } +if slot == 250 { return (28296951008113758208, 18434244275630434304, 18438063358786650112); } +if slot == 251 { return (28410138812146216960, 18438063358786650112, 18441188259278645248); } +if slot == 252 { return (28523326616178671616, 18441188259278645248, 18443618859455678464); } +if slot == 253 { return (28636514420211126272, 18443618859455678464, 18445355067807029248); } +if slot == 254 { return (28749702224243580928, 18445355067807029248, 18446396818965434368); } + } + } + } + } + + return (28862890028276035584, 18446396818965434368, 18446744073709551616); +} + +fn atan(a: u128) -> (u128, u128, u128) { + let slot = a / 129127208515966848; + + if slot < 50 { + if slot < 25 { + if slot < 13 { +if slot == 0 { return (0, 0, 129125099500232464); } +if slot == 1 { return (129127208515966848, 129125099500232464, 258237546393963520); } +if slot == 2 { return (258254417031933696, 258237546393963520, 387324695511873472); } +if slot == 3 { return (387381625547900544, 387324695511873472, 516373916548080576); } +if slot == 4 { return (516508834063867392, 516373916548080576, 645372601464576128); } +if slot == 5 { return (645636042579834368, 645372601464576128, 774308171863002368); } +if slot == 6 { return (774763251095801088, 774308171863002368, 903168086313028096); } +if slot == 7 { return (903890459611767936, 903168086313028096, 1031939847626691200); } +if slot == 8 { return (1033017668127734784, 1031939847626691200, 1160611010068230400); } +if slot == 9 { return (1162144876643701760, 1160611010068230400, 1289169186489097728); } +if slot == 10 { return (1291272085159668736, 1289169186489097728, 1417602055378050304); } +if slot == 11 { return (1420399293675635456, 1417602055378050304, 1545897367816446720); } +if slot == 12 { return (1549526502191602176, 1545897367816446720, 1674042954329124352); } + } else { +if slot == 13 { return (1678653710707569152, 1674042954329124352, 1802026731621518336); } +if slot == 14 { return (1807780919223535872, 1802026731621518336, 1929836709193984000); } +if slot == 15 { return (1936908127739502848, 1929836709193984000, 2057460995824596480); } +if slot == 16 { return (2066035336255469568, 2057460995824596480, 2184887805912064768); } +if slot == 17 { return (2195162544771436288, 2184887805912064768, 2312105465670742016); } +if slot == 18 { return (2324289753287403520, 2312105465670742016, 2439102419170100224); } +if slot == 19 { return (2453416961803369984, 2439102419170100224, 2565867234211449856); } +if slot == 20 { return (2582544170319337472, 2565867234211449856, 2692388608035054592); } +if slot == 21 { return (2711671378835303936, 2692388608035054592, 2818655372851273728); } +if slot == 22 { return (2840798587351270912, 2818655372851273728, 2944656501189739520); } +if slot == 23 { return (2969925795867237376, 2944656501189739520, 3070381111061068288); } +if slot == 24 { return (3099053004383204352, 3070381111061068288, 3195818470926024704); } + } + } else { + if slot < 38 { +if slot == 25 { return (3228180212899171328, 3195818470926024704, 3320958004467535872); } +if slot == 26 { return (3357307421415138304, 3320958004467535872, 3445789295161406976); } +if slot == 27 { return (3486434629931104768, 3445789295161406976, 3570302090642053632); } +if slot == 28 { return (3615561838447071744, 3570302090642053632, 3694486306860029952); } +if slot == 29 { return (3744689046963038208, 3694486306860029952, 3818332032028619776); } +if slot == 30 { return (3873816255479005696, 3818332032028619776, 3941829530357180928); } +if slot == 31 { return (4002943463994972672, 3941829530357180928, 4064969245569435648); } +if slot == 32 { return (4132070672510939136, 4064969245569435648, 4187741804205330944); } +if slot == 33 { return (4261197881026906112, 4187741804205330944, 4310138018705539072); } +if slot == 34 { return (4390325089542872576, 4310138018705539072, 4432148890278133248); } +if slot == 35 { return (4519452298058840064, 4432148890278133248, 4553765611547372032); } +if slot == 36 { return (4648579506574807040, 4553765611547372032, 4674979568984985600); } +if slot == 37 { return (4777706715090774016, 4674979568984985600, 4795782345124729856); } + } else { +if slot == 38 { return (4906833923606739968, 4795782345124729856, 4916165720561409024); } +if slot == 39 { return (5035961132122706944, 4916165720561409024, 5036121675735909376); } +if slot == 40 { return (5165088340638674944, 5036121675735909376, 5155642392508208128); } +if slot == 41 { return (5294215549154640896, 5155642392508208128, 5274720255520642048); } +if slot == 42 { return (5423342757670607872, 5274720255520642048, 5393347853354058752); } +if slot == 43 { return (5552469966186574848, 5393347853354058752, 5511517979479821312); } +if slot == 44 { return (5681597174702541824, 5511517979479821312, 5629223633010935808); } +if slot == 45 { return (5810724383218507776, 5629223633010935808, 5746458019255839744); } +if slot == 46 { return (5939851591734474752, 5746458019255839744, 5863214550078689280); } +if slot == 47 { return (6068978800250441728, 5863214550078689280, 5979486844070228992); } +if slot == 48 { return (6198106008766408704, 5979486844070228992, 6095268726533560320); } +if slot == 49 { return (6327233217282375680, 6095268726533560320, 6210554229289342976); } + } + } + } else { + if slot < 75 { + if slot < 63 { +if slot == 50 { return (6456360425798342656, 6210554229289342976, 6325337590305168384); } +if slot == 51 { return (6585487634314309632, 6325337590305168384, 6439613253154024448); } +if slot == 52 { return (6714614842830276608, 6439613253154024448, 6553375866306938880); } +if slot == 53 { return (6843742051346242560, 6553375866306938880, 6666620282265027584); } +if slot == 54 { return (6972869259862209536, 6666620282265027584, 6779341556536299520); } +if slot == 55 { return (7101996468378177536, 6779341556536299520, 6891534946462701568); } +if slot == 56 { return (7231123676894143488, 6891534946462701568, 7003195909902971904); } +if slot == 57 { return (7360250885410110464, 7003195909902971904, 7114320103776912384); } +if slot == 58 { return (7489378093926076416, 7114320103776912384, 7224903382476832768); } +if slot == 59 { return (7618505302442044416, 7224903382476832768, 7334941796151870464); } +if slot == 60 { return (7747632510958011392, 7334941796151870464, 7444431588871006208); } +if slot == 61 { return (7876759719473977344, 7444431588871006208, 7553369196670551040); } +if slot == 62 { return (8005886927989945344, 7553369196670551040, 7661751245491906560); } + } else { +if slot == 63 { return (8135014136505911296, 7661751245491906560, 7769574549015409664); } +if slot == 64 { return (8264141345021878272, 7769574549015409664, 7876836106395998208); } +if slot == 65 { return (8393268553537846272, 7876836106395998208, 7983533099906451456); } +if slot == 66 { return (8522395762053812224, 7983533099906451456, 8089662892493886464); } +if slot == 67 { return (8651522970569779200, 8089662892493886464, 8195223025255116800); } +if slot == 68 { return (8780650179085745152, 8195223025255116800, 8300211214836463616); } +if slot == 69 { return (8909777387601713152, 8300211214836463616, 8404625350763461632); } +if slot == 70 { return (9038904596117680128, 8404625350763461632, 8508463492705892352); } +if slot == 71 { return (9168031804633646080, 8508463492705892352, 8611723867683410944); } +if slot == 72 { return (9297159013149614080, 8611723867683410944, 8714404867216971776); } +if slot == 73 { return (9426286221665579008, 8714404867216971776, 8816505044431164416); } +if slot == 74 { return (9555413430181548032, 8816505044431164416, 8918023111112380416); } + } + } else { + if slot < 88 { +if slot == 75 { return (9684540638697515008, 8918023111112380416, 9018957934727719936); } +if slot == 76 { return (9813667847213479936, 9018957934727719936, 9119308535409329152); } +if slot == 77 { return (9942795055729448960, 9119308535409329152, 9219074082908748800); } +if slot == 78 { return (10071922264245413888, 9219074082908748800, 9318253893525794816); } +if slot == 79 { return (10201049472761380864, 9318253893525794816, 9416847427016218624); } +if slot == 80 { return (10330176681277349888, 9416847427016218624, 9514854283482376192); } +if slot == 81 { return (10459303889793314816, 9514854283482376192, 9612274200250959872); } +if slot == 82 { return (10588431098309281792, 9612274200250959872, 9709107048741621760); } +if slot == 83 { return (10717558306825248768, 9709107048741621760, 9805352831330324480); } +if slot == 84 { return (10846685515341215744, 9805352831330324480, 9901011678210951168); } +if slot == 85 { return (10975812723857182720, 9901011678210951168, 9996083844258682880); } +if slot == 86 { return (11104939932373149696, 9996083844258682880, 10090569705898426368); } +if slot == 87 { return (11234067140889116672, 10090569705898426368, 10184469757981462528); } + } else { +if slot == 88 { return (11363194349405083648, 10184469757981462528, 10277784610673350656); } +if slot == 89 { return (11492321557921050624, 10277784610673350656, 10370514986355931136); } +if slot == 90 { return (11621448766437015552, 10370514986355931136, 10462661716546199552); } +if slot == 91 { return (11750575974952984576, 10462661716546199552, 10554225738834587648); } +if slot == 92 { return (11879703183468949504, 10554225738834587648, 10645208093845178368); } +if slot == 93 { return (12008830391984916480, 10645208093845178368, 10735609922220072960); } +if slot == 94 { return (12137957600500883456, 10735609922220072960, 10825432461630179328); } +if slot == 95 { return (12267084809016852480, 10825432461630179328, 10914677043814393856); } +if slot == 96 { return (12396212017532817408, 10914677043814393856, 11003345091649155072); } +if slot == 97 { return (12525339226048784384, 11003345091649155072, 11091438116250099712); } +if slot == 98 { return (12654466434564751360, 11091438116250099712, 11178957714107523072); } + } + } + } + + return (12783593643080718336, 11178957714107523072, 11265905564257183744); +} \ No newline at end of file diff --git a/src/math/trig.cairo b/src/math/trig.cairo index 1de0e1d..de0f1d9 100644 --- a/src/math/trig.cairo +++ b/src/math/trig.cairo @@ -1,8 +1,9 @@ +use integer::{u128_safe_divmod, u128_as_non_zero}; use option::OptionTrait; +use cubit::math::lut; use cubit::types::fixed::{Fixed, FixedTrait, FixedAdd, FixedSub, FixedMul, FixedDiv, ONE_u128}; - // CONSTANTS const PI_u128: u128 = 57952155664616982739_u128; @@ -24,6 +25,18 @@ fn acos(a: Fixed) -> Fixed { } } +fn acos_fast(a: Fixed) -> Fixed { + assert(a.mag <= ONE_u128, 'out of range'); + let asin_arg = (FixedTrait::one() - a * a).sqrt(); + let asin_res = asin_fast(asin_arg); + + if (a.sign) { + return FixedTrait::new(PI_u128, false) - asin_res; + } else { + return asin_res; + } +} + // Calculates arcsin(a) for -1 <= a <= 1 (fixed point) // arcsin(a) = arctan(a / sqrt(1 - a^2)) fn asin(a: Fixed) -> Fixed { @@ -37,6 +50,17 @@ fn asin(a: Fixed) -> Fixed { return atan(a / div); } +fn asin_fast(a: Fixed) -> Fixed { + assert(a.mag <= ONE_u128, 'out of range'); + + if (a.mag == ONE_u128) { + return FixedTrait::new(HALF_PI_u128, a.sign); + } + + let div = (FixedTrait::one() - a * a).sqrt(); + return atan_fast(a / div); +} + // Calculates arctan(a) (fixed point) // See https://stackoverflow.com/a/50894477 for range adjustments fn atan(a: Fixed) -> Fixed { @@ -80,20 +104,54 @@ fn atan(a: Fixed) -> Fixed { return FixedTrait::new(res.mag, a.sign); } +fn atan_fast(a: Fixed) -> Fixed { + let mut at = a.abs(); + let mut shift = false; + let mut invert = false; + + // Invert value when a > 1 + if (at.mag > ONE_u128) { + at = FixedTrait::one() / at; + invert = true; + } + + // Account for lack of precision in polynomaial when a > 0.7 + if (at.mag > 12912720851596686131) { + let sqrt3_3 = FixedTrait::new(10650232656328343401, false); // sqrt(3) / 3 + at = (at - sqrt3_3) / (FixedTrait::one() + at * sqrt3_3); + shift = true; + } + + let (start, low, high) = lut::atan(at.mag); + let partial_step = FixedTrait::new(at.mag - start, false) / FixedTrait::new(129127208515966848, false); + let mut res = partial_step * FixedTrait::new(high - low, false) + FixedTrait::new(low, false); + + // Adjust for sign change, inversion, and shift + if (shift) { + res = res + FixedTrait::new(9658692610769497123, false); // pi / 6 + } + + if (invert) { + res = res - FixedTrait::new(HALF_PI_u128, false); + } + + return FixedTrait::new(res.mag, a.sign); +} + // Calculates cos(a) with a in radians (fixed point) fn cos(a: Fixed) -> Fixed { return sin(FixedTrait::new(HALF_PI_u128, false) - a); } +fn cos_fast(a: Fixed) -> Fixed { + return sin_fast(FixedTrait::new(HALF_PI_u128, false) - a); +} + fn sin(a: Fixed) -> Fixed { let a1_u128 = a.mag % (2 * PI_u128); - let whole_rem = a1_u128 / PI_u128; - let a2 = FixedTrait::new(a1_u128 % PI_u128, false); - let mut partial_sign = false; - - if (whole_rem == 1) { - partial_sign = true; - } + let (whole_rem, partial_rem) = u128_safe_divmod(a1_u128, u128_as_non_zero(PI_u128)); + let a2 = FixedTrait::new(partial_rem, false); + let partial_sign = whole_rem == 1; let acc = Fixed { mag: ONE_u128, sign: false }; let loop_res = a2 * _sin_loop(a2, 7, acc); @@ -101,6 +159,23 @@ fn sin(a: Fixed) -> Fixed { return FixedTrait::new(loop_res.mag, res_sign); } +fn sin_fast(a: Fixed) -> Fixed { + let a1_u128 = a.mag % (2 * PI_u128); + let (whole_rem, mut partial_rem) = u128_safe_divmod(a1_u128, u128_as_non_zero(PI_u128)); + let partial_sign = whole_rem == 1; + + if partial_rem >= PI_u128 { + partial_rem = PI_u128 - partial_rem; + } + + let (start, low, high) = lut::sin(partial_rem); + let partial_step = FixedTrait::new(partial_rem - start, false) / FixedTrait::new(113187804032455040, false); + let res = partial_step * (FixedTrait::new(high, false) - FixedTrait::new(low, false)) + FixedTrait::new(low, false); + + let res_sign = a.sign ^ partial_sign; + return FixedTrait::new(res.mag, res_sign); +} + // Calculates tan(a) with a in radians (fixed point) fn tan(a: Fixed) -> Fixed { let sinx = sin(a); @@ -109,6 +184,13 @@ fn tan(a: Fixed) -> Fixed { return sinx / cosx; } +fn tan_fast(a: Fixed) -> Fixed { + let sinx = sin_fast(a); + let cosx = cos_fast(a); + assert(cosx.mag != 0, 'tan undefined'); + return sinx / cosx; +} + // Helper function to calculate Taylor series for sin fn _sin_loop(a: Fixed, i: u128, acc: Fixed) -> Fixed { let div_u128 = (2 * i + 2) * (2 * i + 3); @@ -126,7 +208,7 @@ fn _sin_loop(a: Fixed, i: u128, acc: Fixed) -> Fixed { use traits::Into; -use cubit::test::helpers::assert_precise; +use cubit::test::helpers::{assert_precise, assert_relative}; use cubit::types::fixed::{ONE, FixedInto, FixedPartialEq, FixedPrint}; #[test] @@ -157,28 +239,57 @@ fn test_acos_fail() { } #[test] -#[available_gas(3000000)] +#[available_gas(1400000)] +fn test_atan_fast() { + let error = Option::Some(184467440737095); + + let a = FixedTrait::new(2 * ONE_u128, false); + assert_relative(atan_fast(a), 20423289048683266000, 'invalid two', error); + + let a = FixedTrait::one(); + assert_relative(atan_fast(a), 14488038916154245000, 'invalid one', error); + + let a = FixedTrait::new(ONE_u128 / 2, false); + assert_relative(atan_fast(a), 8552788783625223000, 'invalid half', error); + + let a = FixedTrait::zero(); + assert(atan_fast(a).into() == 0, 'invalid zero'); + + let a = FixedTrait::new(ONE_u128 / 2, true); + assert_relative(atan_fast(a), -8552788783625223000, 'invalid neg half', error); + + let a = FixedTrait::new(ONE_u128, true); + assert_relative(atan_fast(a), -14488038916154245000, 'invalid neg one', error); + + let a = FixedTrait::new(2 * ONE_u128, true); + assert_relative(atan_fast(a), -20423289048683266000, 'invalid neg two', error); +} + +use debug::PrintTrait; + +#[test] +#[available_gas(2600000)] fn test_atan() { let a = FixedTrait::new(2 * ONE_u128, false); - assert_precise(atan(a), 20423289048683266000, 'invalid two', Option::None(())); + assert_relative(atan(a), 20423289048683266000, 'invalid two', Option::None(())); let a = FixedTrait::one(); - assert_precise(atan(a), 14488038916154245000, 'invalid one', Option::None(())); + assert_relative(atan(a), 14488038916154245000, 'invalid one', Option::None(())); let a = FixedTrait::new(ONE_u128 / 2, false); - assert_precise(atan(a), 8552788783625223000, 'invalid half', Option::None(())); + assert_relative(atan(a), 8552788783625223000, 'invalid half', Option::None(())); let a = FixedTrait::zero(); assert(atan(a).into() == 0, 'invalid zero'); let a = FixedTrait::new(ONE_u128 / 2, true); - assert_precise(atan(a), -8552788783625223000, 'invalid neg half', Option::None(())); + assert_relative(atan(a), -8552788783625223000, 'invalid neg half', Option::None(())); let a = FixedTrait::new(ONE_u128, true); - assert_precise(atan(a), -14488038916154245000, 'invalid neg one', Option::None(())); + assert_relative(atan(a), -14488038916154245000, 'invalid neg one', Option::None(())); let a = FixedTrait::new(2 * ONE_u128, true); - assert_precise(atan(a), -20423289048683266000, 'invalid neg two', Option::None(())); + assert_relative(atan(a), -20423289048683266000, 'invalid neg two', Option::None(())); } #[test] @@ -266,6 +377,30 @@ fn test_sin() { ); // 0.9613974918793389 } +#[test] +#[available_gas(1000000)] +fn test_sin_fast() { + let error = Option::Some(184467440737095); + + let a = FixedTrait::new(HALF_PI_u128, false); + assert_precise(sin_fast(a), ONE, 'invalid half pi', error); + + let a = FixedTrait::new(HALF_PI_u128 / 2, false); + assert_precise(sin_fast(a), 13043817825332781000, 'invalid quarter pi', error); // 0.7071067811865475 + + let a = FixedTrait::new(PI_u128, false); + assert(sin_fast(a).into() == 0, 'invalid pi'); + + let a = FixedTrait::new(HALF_PI_u128, true); + assert_precise(sin_fast(a), -ONE, 'invalid neg half pi', error); // 0.9999999999939766 + + let a = FixedTrait::new_unscaled(17, false); + assert_precise(sin_fast(a), -17734653485808441000, 'invalid 17', error); // -0.9613974918793389 + + let a = FixedTrait::new_unscaled(17, true); + assert_precise(sin_fast(a), 17734653485808441000, 'invalid -17', error); // 0.9613974918793389 +} + #[test] #[available_gas(8000000)] fn test_tan() { diff --git a/src/test/helpers.cairo b/src/test/helpers.cairo index be5f04b..55733ae 100644 --- a/src/test/helpers.cairo +++ b/src/test/helpers.cairo @@ -3,7 +3,7 @@ use traits::Into; use cubit::types::fixed::{Fixed, FixedTrait, FixedSub, FixedPartialEq, FixedPrint}; -const DEFAULT_PRECISION: u128 = 1844674407370_u128; // 1e-7 +const DEFAULT_PRECISION: u128 = 1844674407370; // 1e-7 // To use `DEFAULT_PRECISION`, final arg is: `Option::None(())`. // To use `custom_precision` of 184467440737_u128: `Option::Some(184467440737_u128)`.