#![allow(dead_code)]
pub const SCALE: u128 = 1_000_000_000_000u128;
pub const SCALE_I: i128 = 1_000_000_000_000i128; pub const SCALE_6: i64 = 1_000_000i64; pub const SCALE_HP_U: u128 = 1_000_000_000_000_000u128; pub const SCALE_HP: i128 = 1_000_000_000_000_000i128; pub const HP_TO_STD: i128 = 1_000i128; pub const LN2: u128 = 693_147_180_560u128; pub const LN2_I: i128 = 693_147_180_560i128; pub const LN2_HP: i128 = 693_147_180_559_945i128; pub const HALF_LN2_HP: i128 = 346_573_590_279_973i128; pub const INV_SQRT2: u128 = 707_106_781_187u128; pub const SQRT2_SCALE: u128 = 1_414_213_562_373u128; pub const INV_SQRT2_HP: u128 = 707_106_781_186_548u128; pub const SQRT2_SCALE_HP: u128 = 1_414_213_562_373_095u128; pub const INV_SQRT_2PI_HP: i128 = 398_942_280_401_433i128; pub const LN_2PI: i128 = 1_837_877_066_409i128; pub const LN_HP_TERMS: usize = 12;
pub const EXP_HP_TERMS: i128 = 15;
pub const LN2_6: i64 = 693_147; pub const PI6: i64 = 3_141_593;
pub const PI2_6: i64 = 6_283_185;
pub const PIH_6: i64 = 1_570_796;
pub const PIQ_6: i64 = 785_398;
pub const SC0_6: i64 = 1_000_000;
pub const SC1_6: i64 = -166_667;
pub const SC2_6: i64 = 8_333;
pub const SC3_6: i64 = -198;
pub const SC4_6: i64 = 3;
pub const CC0_6: i64 = 1_000_000;
pub const CC1_6: i64 = -500_000;
pub const CC2_6: i64 = 41_667;
pub const CC3_6: i64 = -1_389;
pub const CC4_6: i64 = 25;
pub const FP_DIV_THIN_MAX: u128 = u128::MAX / SCALE;
pub const LN_REMEZ_W0: i128 = 1_000_000_000_000;
pub const LN_REMEZ_W1: i128 = 333_333_333_406;
pub const LN_REMEZ_W2: i128 = 199_999_986_321;
pub const LN_REMEZ_W3: i128 = 142_858_120_138;
pub const LN_REMEZ_W4: i128 = 111_077_000_901;
pub const LN_REMEZ_W5: i128 = 91_550_218_194;
pub const LN_REMEZ_W6: i128 = 70_353_796_623;
pub const LN_REMEZ_W7: i128 = 100_340_778_474;
pub const LN_REMEZ_HP0: i128 = 1_000_000_000_000_000;
pub const LN_REMEZ_HP1: i128 = 333_333_333_333_413;
pub const LN_REMEZ_HP2: i128 = 199_999_999_976_438;
pub const LN_REMEZ_HP3: i128 = 142_857_145_558_286;
pub const LN_REMEZ_HP4: i128 = 111_110_954_306_731;
pub const LN_REMEZ_HP5: i128 = 90_914_299_002_370;
pub const LN_REMEZ_HP6: i128 = 76_818_381_244_458;
pub const LN_REMEZ_HP7: i128 = 67_956_527_427_899;
pub const LN_REMEZ_HP8: i128 = 49_403_945_243_824;
pub const LN_REMEZ_HP9: i128 = 88_903_819_130_567;
pub const LN2_LO: i128 = -54_690_582_768;
pub const LN2_HP_LO: i128 = 309_417_232_121_458;
pub const LN_REMEZ_COEFFS: [i128; 8] = [
LN_REMEZ_W0,
LN_REMEZ_W1,
LN_REMEZ_W2,
LN_REMEZ_W3,
LN_REMEZ_W4,
LN_REMEZ_W5,
LN_REMEZ_W6,
LN_REMEZ_W7,
];
pub const LN_REMEZ_HP_COEFFS: [i128; 10] = [
LN_REMEZ_HP0,
LN_REMEZ_HP1,
LN_REMEZ_HP2,
LN_REMEZ_HP3,
LN_REMEZ_HP4,
LN_REMEZ_HP5,
LN_REMEZ_HP6,
LN_REMEZ_HP7,
LN_REMEZ_HP8,
LN_REMEZ_HP9,
];
pub const EXP_REMEZ_P1: i128 = 166_666_666_667;
pub const EXP_REMEZ_P2: i128 = -2_777_777_778;
pub const EXP_REMEZ_P3: i128 = 66_137_563;
pub const EXP_REMEZ_P4: i128 = -1_653_390;
pub const EXP_REMEZ_P5: i128 = 41_381;
pub const EXP_REMEZ_HP_P1: i128 = 166_666_666_666_666;
pub const EXP_REMEZ_HP_P2: i128 = -2_777_777_777_702;
pub const EXP_REMEZ_HP_P3: i128 = 66_137_563_214;
pub const EXP_REMEZ_HP_P4: i128 = -1_653_390_221;
pub const EXP_REMEZ_HP_P5: i128 = 41_381_368;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MulDivRounding {
ToZero,
Floor,
Ceil,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct U256 {
pub limbs: [u64; 4],
}
#[inline]
fn assemble_u128_product(p00: u128, p01: u128, p10: u128, p11: u128) -> U256 {
const MASK: u128 = u64::MAX as u128;
let column1 = (p00 >> 64) + (p01 & MASK) + (p10 & MASK);
let column2 = (p01 >> 64) + (p10 >> 64) + (p11 & MASK) + (column1 >> 64);
let column3 = (p11 >> 64) + (column2 >> 64);
U256 {
limbs: [p00 as u64, column1 as u64, column2 as u64, column3 as u64],
}
}
#[inline]
fn div_rem_u64_step(remainder: u64, limb: u64, divisor: u64) -> (u64, u64) {
debug_assert!(divisor != 0);
debug_assert!(remainder < divisor);
let current = ((remainder as u128) << 64) | limb as u128;
(
(current / divisor as u128) as u64,
(current % divisor as u128) as u64,
)
}
#[inline]
fn refine_knuth_quotient_digit(
mut qhat: u128,
mut rhat: u128,
u0: u64,
v0: u64,
v1: u64,
) -> (u128, u128) {
const BASE: u128 = 1u128 << 64;
debug_assert!(qhat < BASE);
debug_assert!(u128::from(v1) >= BASE / 2);
if rhat < BASE {
while qhat * u128::from(v0) > (rhat << 64) + u128::from(u0) {
debug_assert!(qhat > 0);
(qhat, rhat) = advance_knuth_refinement(qhat, rhat, v1);
if rhat >= BASE {
break;
}
}
}
(qhat, rhat)
}
#[inline]
fn advance_knuth_refinement(qhat: u128, rhat: u128, v1: u64) -> (u128, u128) {
const BASE: u128 = 1u128 << 64;
debug_assert!(qhat > 0);
debug_assert!(rhat < BASE);
debug_assert!(u128::from(v1) >= BASE / 2);
(qhat - 1, rhat + u128::from(v1))
}
impl U256 {
#[inline]
pub const fn zero() -> Self {
Self { limbs: [0; 4] }
}
#[inline]
pub fn from_u128(x: u128) -> Self {
Self {
limbs: [x as u64, (x >> 64) as u64, 0, 0],
}
}
#[inline]
pub fn low_u128(&self) -> u128 {
(self.limbs[0] as u128) | ((self.limbs[1] as u128) << 64)
}
#[inline]
pub fn high_u128_nonzero(&self) -> bool {
self.limbs[2] != 0 || self.limbs[3] != 0
}
#[inline]
pub fn bit(&self, idx: usize) -> bool {
let limb = idx / 64;
let shift = idx % 64;
((self.limbs[limb] >> shift) & 1) != 0
}
#[inline]
pub fn set_bit(&mut self, idx: usize) {
let limb = idx / 64;
let shift = idx % 64;
self.limbs[limb] |= 1u64 << shift;
}
#[inline]
pub fn shl1(&mut self) {
let mut carry = 0u64;
for limb in &mut self.limbs {
let next = *limb >> 63;
*limb = (*limb << 1) | carry;
carry = next;
}
}
#[inline]
pub fn overflowing_sub_assign(&mut self, rhs: &Self) -> bool {
let mut borrow = 0u128;
for i in 0..4 {
let lhs = self.limbs[i] as u128;
let rhs_i = rhs.limbs[i] as u128;
let sub = rhs_i + borrow;
if lhs >= sub {
self.limbs[i] = (lhs - sub) as u64;
borrow = 0;
} else {
self.limbs[i] = ((1u128 << 64) + lhs - sub) as u64;
borrow = 1;
}
}
borrow != 0
}
#[inline]
pub fn mul_u128(a: u128, b: u128) -> Self {
let a0 = a as u64 as u128;
let a1 = (a >> 64) as u64 as u128;
let b0 = b as u64 as u128;
let b1 = (b >> 64) as u64 as u128;
assemble_u128_product(a0 * b0, a0 * b1, a1 * b0, a1 * b1)
}
#[inline]
pub fn cmp_words(&self, other: &Self) -> core::cmp::Ordering {
for i in (0..4).rev() {
match self.limbs[i].cmp(&other.limbs[i]) {
core::cmp::Ordering::Equal => continue,
non_eq => return non_eq,
}
}
core::cmp::Ordering::Equal
}
#[inline]
pub fn ge(&self, other: &Self) -> bool {
self.cmp_words(other) != core::cmp::Ordering::Less
}
#[inline]
pub fn div_rem_u64(&self, divisor: u64) -> (Self, u64) {
let mut quo = [0u64; 4];
let mut rem = 0u64;
for i in (0..4).rev() {
(quo[i], rem) = div_rem_u64_step(rem, self.limbs[i], divisor);
}
(Self { limbs: quo }, rem)
}
#[inline]
pub fn div_rem_u128(&self, divisor: u128) -> (Self, u128) {
const BASE: u128 = 1u128 << 64;
let d0 = divisor as u64;
let d1 = (divisor >> 64) as u64;
debug_assert!(d1 != 0);
let shift = d1.leading_zeros();
let (v0, v1) = if shift == 0 {
(d0, d1)
} else {
(d0 << shift, (d1 << shift) | (d0 >> (64 - shift)))
};
let mut u = [0u64; 5];
if shift == 0 {
u[..4].copy_from_slice(&self.limbs);
} else {
let mut carry = 0u64;
for (i, limb) in self.limbs.iter().copied().enumerate() {
u[i] = (limb << shift) | carry;
carry = limb >> (64 - shift);
}
u[4] = carry;
}
let mut q = [0u64; 3];
let v1_u128 = v1 as u128;
let v0_u128 = v0 as u128;
for j in (0..=2).rev() {
let u2 = u[j + 2] as u128;
let u1 = u[j + 1] as u128;
let u0 = u[j] as u128;
let numerator_hat = (u2 << 64) | u1;
let mut qhat = numerator_hat / v1_u128;
let mut rhat = numerator_hat % v1_u128;
if qhat >= BASE {
qhat = BASE - 1;
rhat = numerator_hat - qhat * v1_u128;
}
let (refined_qhat, _) = refine_knuth_quotient_digit(qhat, rhat, u0 as u64, v0, v1);
qhat = refined_qhat;
let mut carry = 0u128;
let mut borrow = 0u128;
for (offset, v_limb) in [v0, v1].into_iter().enumerate() {
let product = qhat * (v_limb as u128) + carry;
carry = product >> 64;
let sub = (product & (BASE - 1)) + borrow;
let cur = u[j + offset] as u128;
if cur >= sub {
u[j + offset] = (cur - sub) as u64;
borrow = 0;
} else {
u[j + offset] = (BASE + cur - sub) as u64;
borrow = 1;
}
}
let top_sub = carry + borrow;
let top = u[j + 2] as u128;
let mut qhat_u64 = qhat as u64;
if top >= top_sub {
u[j + 2] = (top - top_sub) as u64;
} else {
qhat_u64 = qhat_u64.wrapping_sub(1);
let sum0 = u[j] as u128 + v0_u128;
u[j] = sum0 as u64;
let carry0 = sum0 >> 64;
let sum1 = u[j + 1] as u128 + v1_u128 + carry0;
u[j + 1] = sum1 as u64;
let carry1 = sum1 >> 64;
u[j + 2] = (top + BASE - top_sub + carry1) as u64;
}
q[j] = qhat_u64;
}
let rem_norm = (u[0] as u128) | ((u[1] as u128) << 64);
let rem = if shift == 0 {
rem_norm
} else {
rem_norm >> shift
};
(
Self {
limbs: [q[0], q[1], q[2], 0],
},
rem,
)
}
}
#[cfg(kani)]
mod verification {
use super::*;
#[kani::proof]
fn u256_product_carry_assembly_is_exact() {
const BASE: u128 = 1u128 << 64;
const MASK: u128 = BASE - 1;
const MAX_PARTIAL: u128 = MASK * MASK;
let p00: u128 = kani::any();
let p01: u128 = kani::any();
let p10: u128 = kani::any();
let p11: u128 = kani::any();
kani::assume(p00 <= MAX_PARTIAL);
kani::assume(p01 <= MAX_PARTIAL);
kani::assume(p10 <= MAX_PARTIAL);
kani::assume(p11 <= MAX_PARTIAL);
let product = assemble_u128_product(p00, p01, p10, p11);
let carry0 = p00 >> 64;
let (middle_a, carry_a) = p01.overflowing_add(p10);
let (middle, carry_b) = middle_a.overflowing_add(carry0);
let carry1 = (middle >> 64) + (u128::from(carry_a) + u128::from(carry_b)) * BASE;
let upper = p11 + carry1;
assert_eq!(product.limbs[0], (p00 & MASK) as u64);
assert_eq!(product.limbs[1], (middle & MASK) as u64);
assert_eq!(product.limbs[2], (upper & MASK) as u64);
assert_eq!(product.limbs[3], (upper >> 64) as u64);
}
#[kani::proof]
fn u256_div_rem_u64_digit_fits_one_limb() {
let remainder: u64 = kani::any();
let limb: u64 = kani::any();
let divisor: u64 = kani::any();
kani::assume(divisor != 0);
kani::assume(remainder < divisor);
let current = ((remainder as u128) << 64) | limb as u128;
let exclusive_bound = (divisor as u128) << 64;
assert!(current < exclusive_bound);
}
#[kani::proof]
fn knuth_d3_two_corrections_cross_the_base() {
const BASE: u128 = 1u128 << 64;
let qhat: u64 = kani::any();
let rhat: u128 = kani::any();
let v1: u64 = kani::any();
kani::assume(qhat > 0);
kani::assume(rhat < BASE);
kani::assume(v1 >= 1u64 << 63);
let (qhat1, rhat1) = advance_knuth_refinement(u128::from(qhat), rhat, v1);
assert!(rhat1 < 2 * BASE);
assert_eq!(qhat1 + 1, u128::from(qhat));
if rhat1 < BASE && qhat1 > 0 {
let (qhat2, rhat2) = advance_knuth_refinement(qhat1, rhat1, v1);
assert!(rhat2 >= BASE);
assert!(rhat2 < 2 * BASE);
assert_eq!(qhat2 + 2, u128::from(qhat));
}
}
}
#[cfg(test)]
pub const GL3_NODES: [i128; 3] = [-774596669241, 0, 774596669241];
#[cfg(test)]
pub const GL3_WEIGHTS: [i128; 3] = [555555555556, 888888888889, 555555555556];
#[cfg(test)]
pub const GL5_NODES: [i128; 5] = [-906179845939, -538469310106, 0, 538469310106, 906179845939];
#[cfg(test)]
pub const GL5_WEIGHTS: [i128; 5] = [
236926885056,
478628670499,
568888888889,
478628670499,
236926885056,
];
#[cfg(test)]
pub const GL7_NODES: [i128; 7] = [
-949107912343,
-741531185599,
-405845151377,
0,
405845151377,
741531185599,
949107912343,
];
#[cfg(test)]
pub const GL7_WEIGHTS: [i128; 7] = [
129484966169,
279705391489,
381830050505,
417959183673,
381830050505,
279705391489,
129484966169,
];
pub const PI_SCALE: i128 = 3_141_592_653_590;
pub const PI_OVER_2_SCALE: i128 = 1_570_796_326_795;
pub const PI_OVER_4_SCALE: i128 = 785_398_163_397;
pub const TWO_PI_SCALE: i128 = 6_283_185_307_180;
pub const TWO_PI_LO: i128 = -413_523_074_713;
pub const SIN_C1: i128 = 1_000_000_000_000;
pub const SIN_C3: i128 = -166_666_666_666;
pub const SIN_C5: i128 = 8_333_333_325;
pub const SIN_C7: i128 = -198_412_638;
pub const SIN_C9: i128 = 2_755_535;
pub const SIN_C11: i128 = -24_761;
pub const COS_C0: i128 = 1_000_000_000_000;
pub const COS_C2: i128 = -499_999_999_995;
pub const COS_C4: i128 = 41_666_666_560;
pub const COS_C6: i128 = -1_388_888_100;
pub const COS_C8: i128 = 24_799_027;
pub const COS_C10: i128 = -271_792;
pub const INV_SQRT_2PI: i128 = 398_942_280_401;
pub const P_CDF: i128 = 231_641_900_000;
pub const B1: i128 = 319_381_530_000;
pub const B2: i128 = -356_563_782_000;
pub const B3: i128 = 1_781_477_937_000;
pub const B4: i128 = -1_821_255_978_000;
pub const B5: i128 = 1_330_274_429_000;
pub const POLY_I0_HI: i128 = 500_000_000_000; pub const POLY_I1_HI: i128 = 1_500_000_000_000; pub const POLY_I2_HI: i128 = 3_000_000_000_000; pub const POLY_I3_HI: i128 = 5_000_000_000_000;
pub const POLY_I0_MID: i128 = 250_000_000_000; pub const POLY_I0_HW: i128 = 250_000_000_000; pub const POLY_I1_MID: i128 = 1_000_000_000_000; pub const POLY_I1_HW: i128 = 500_000_000_000; pub const POLY_I2_MID: i128 = 2_250_000_000_000; pub const POLY_I2_HW: i128 = 750_000_000_000; pub const POLY_I3_MID: i128 = 4_000_000_000_000; pub const POLY_I3_HW: i128 = 1_000_000_000_000; pub const POLY_I4_MID: i128 = 6_500_000_000_000; pub const POLY_I4_HW: i128 = 1_500_000_000_000;
pub const POLY_I0: [i128; 12] = [
598_706_325_683, 96_667_029_201, -3_020_844_663, -944_013_957, 46_217_350, 8_272_414, -471_315, -57_343, 3_604, 323, -22, -2, ];
pub const POLY_I1: [i128; 12] = [
841_344_746_069, 120_985_362_260, -30_246_340_569, -2, 1_260_264_237, -126_026_396, -31_506_801, 6_001_162, 469_221, -171_725, -2_154, 3_430, ];
pub const POLY_I2: [i128; 12] = [
987_775_527_333, 23_804_738_909, -20_085_247_612, 9_066_257_242, -1_941_844_732, -109_591_587, 176_499_579, -35_211_549, -3_281_426, 2_537_867, -202_799, -79_270, ];
pub const POLY_I3: [i128; 12] = [
999_968_328_754, 133_830_262, -267_660_226, 334_574_933, -289_967_550, 181_788_193, -82_521_635, 25_519_844, -3_928_611, -756_992, 656_918, -150_548, ];
pub const POLY_I4: [i128; 12] = [
999_999_999_967, 392, -2_500, 6_511, -7_968, 22_918, -64_874, 57_376, 4_139, 19_426, -72_091, 36_710, ];
pub const POLY_HP_V2_I0_HI: i128 = 500_000_000_000_000;
pub const POLY_HP_V2_I1_HI: i128 = 1_500_000_000_000_000;
pub const POLY_HP_V2_I2A_HI: i128 = 2_250_000_000_000_000;
pub const POLY_HP_V2_I2B_HI: i128 = 3_000_000_000_000_000;
pub const POLY_HP_V2_I3A_HI: i128 = 4_000_000_000_000_000;
pub const POLY_HP_V2_I0_MID: i128 = 250_000_000_000_000;
pub const POLY_HP_V2_I0_HW: i128 = 250_000_000_000_000;
pub const POLY_HP_V2_I1_MID: i128 = 1_000_000_000_000_000;
pub const POLY_HP_V2_I1_HW: i128 = 500_000_000_000_000;
pub const POLY_HP_V2_I2A_MID: i128 = 1_875_000_000_000_000;
pub const POLY_HP_V2_I2A_HW: i128 = 375_000_000_000_000;
pub const POLY_HP_V2_I2B_MID: i128 = 2_625_000_000_000_000;
pub const POLY_HP_V2_I2B_HW: i128 = 375_000_000_000_000;
pub const POLY_HP_V2_I3A_MID: i128 = 3_500_000_000_000_000;
pub const POLY_HP_V2_I3A_HW: i128 = 500_000_000_000_000;
pub const POLY_HP_V2_I3B_MID: i128 = 4_500_000_000_000_000;
pub const POLY_HP_V2_I3B_HW: i128 = 500_000_000_000_000;
pub const POLY_HP_V2_I0: [i128; 14] = [
598706325682924,
96667029200713,
-3020844662519,
-944013957034,
46217349936,
8272413929,
-471315375,
-57342341,
3603759,
323049,
-21723,
-1210,
4,
-100,
];
pub const POLY_HP_V2_I1: [i128; 14] = [
841344746068544,
120985362259572,
-30246340565023,
-33,
1260264191835,
-126026418616,
-31506612696,
6001256127,
468867241,
-171906606,
-1847116,
3593915,
-100208,
-55795,
];
pub const POLY_HP_V2_I2A: [i128; 16] = [
969603638234739,
25794853435008,
-9068503160759,
1520863550899,
-54796253049,
-24374992132,
3883873087,
17940493,
-60092044,
4454506,
433779,
-86366,
3277,
5989,
-982,
-1491,
];
pub const POLY_HP_V2_I2B: [i128; 16] = [
995667551636987,
4771568098811,
-2348506173644,
658769960927,
-107076056464,
7184669478,
828940223,
-236847082,
16656613,
1411389,
-351615,
22524,
5391,
-5946,
-829,
1606,
];
pub const POLY_HP_V2_I3A: [i128; 18] = [
999767370920965,
436341347522,
-381798679096,
204535006650,
-73575786827,
18081462775,
-2821236035,
167169387,
39009455,
-11645489,
1152149,
49566,
-6912,
5795,
-13949,
-1724,
3518,
416,
];
pub const POLY_HP_V2_I3B: [i128; 18] = [
999996602326874,
7991870552,
-8990854373,
6410146188,
-3231088291,
1213608926,
-347400516,
75547720,
-11941734,
1139090,
6868,
-3204,
4445,
-25983,
39,
15785,
-15,
-3944,
];
pub const POLY_DERIV_I0: [i128; 11] = [
96_667_029_201, -6_041_689_325, -2_832_041_871, 184_869_400, 41_362_070, -2_827_892, -401_398, 28_830, 2_911, -217, -17, ];
pub const POLY_DERIV_I1: [i128; 11] = [
120_985_362_260, -60_492_681_138, -7, 5_041_056_948, -630_131_980, -189_040_804, 42_008_136, 3_753_767, -1_545_525, -21_543, 37_729, ];
pub const POLY_DERIV_I2: [i128; 11] = [
23_804_738_909, -40_170_495_223, 27_198_771_725, -7_767_378_928, -547_957_935, 1_058_997_473, -246_480_840, -26_251_407, 22_840_807, -2_027_989, -871_975, ];
pub const POLY_DERIV_I3: [i128; 11] = [
133_830_262, -535_320_452, 1_003_724_800, -1_159_870_200, 908_940_964, -495_129_811, 178_638_909, -31_428_892, -6_812_924, 6_569_175, -1_656_032, ];
pub const POLY_DERIV_I4: [i128; 11] = [
392, -4_999, 19_533, -31_870, 114_588, -389_245, 401_630, 33_110, 174_838, -720_908, 403_807, ];
pub const POLY_HP_I0_HI: i128 = 500_000_000_000_000; pub const POLY_HP_I1_HI: i128 = 1_500_000_000_000_000; pub const POLY_HP_I2A_HI: i128 = 2_250_000_000_000_000; pub const POLY_HP_I2B_HI: i128 = 3_000_000_000_000_000; pub const POLY_HP_I3_HI: i128 = 5_000_000_000_000_000; pub const POLY_HP_I4_HI: i128 = 8_000_000_000_000_000;
pub const POLY_HP_I0_MID: i128 = 250_000_000_000_000; pub const POLY_HP_I0_HW: i128 = 250_000_000_000_000; pub const POLY_HP_I1_MID: i128 = 1_000_000_000_000_000; pub const POLY_HP_I1_HW: i128 = 500_000_000_000_000; pub const POLY_HP_I2A_MID: i128 = 1_875_000_000_000_000; pub const POLY_HP_I2A_HW: i128 = 375_000_000_000_000; pub const POLY_HP_I2B_MID: i128 = 2_625_000_000_000_000; pub const POLY_HP_I2B_HW: i128 = 375_000_000_000_000; pub const POLY_HP_I3_MID: i128 = 4_000_000_000_000_000; pub const POLY_HP_I3_HW: i128 = 1_000_000_000_000_000; pub const POLY_HP_I4_MID: i128 = 6_500_000_000_000_000; pub const POLY_HP_I4_HW: i128 = 1_500_000_000_000_000;
pub const POLY_HP_I0: [i128; 11] = [
598_706_325_682_924,
96_667_029_200_696,
-3_020_844_662_516,
-944_013_956_714,
46_217_349_913,
8_272_412_155,
-471_315_308,
-57_338_363,
3_603_676,
319_234,
-21_682,
];
pub const POLY_HP_I1: [i128; 14] = [
841_344_746_068_543,
120_985_362_259_573,
-30_246_340_564_937,
-42,
1_260_264_190_911,
-126_026_418_615,
-31_506_609_021,
6_001_256_288,
468_860_551,
-171_907_068,
-1_841_451,
3_594_401,
-102_019,
-55_972,
];
pub const POLY_HP_I2A: [i128; 16] = [
969_603_638_234_739,
25_794_853_435_009,
-9_068_503_160_746,
1_520_863_550_917,
-54_796_253_230,
-24_374_992_417,
3_883_874_172,
17_942_200,
-60_095_146,
4_449_630,
438_328,
-79_214,
-32,
787,
-37,
-5,
];
pub const POLY_HP_I2B: [i128; 16] = [
995_667_551_636_987,
4_771_568_098_812,
-2_348_506_173_634,
658_769_960_908,
-107_076_056_623,
7_184_669_785,
828_941_175,
-236_848_923,
16_653_893,
1_416_649,
-347_626,
14_809,
2_490,
-335,
0,
3,
];
pub const POLY_HP_I3: [i128; 18] = [
999_968_328_758_167,
133_830_225_765,
-267_660_451_532,
334_575_564_403,
-289_965_489_107,
181_786_056_822,
-82_528_639_763,
25_518_023_995,
-3_916_655_477,
-740_177_127,
644_211_338,
-173_713_498,
9_112_129,
9_466_972,
-3_317_226,
279_600,
129_868,
-36_902,
];
pub const POLY_HP_I4_PDF: [i128; 18] = [
266_955,
-2_602_817,
12_388_515,
-38_310_277,
86_409_890,
-151_264_254,
213_430_049,
-248_623_071,
242_831_742,
-201_047_855,
141_817_893,
-84_256_548,
41_133_871,
-16_731_484,
5_765_684,
-839_426,
-684_839,
315_977,
];
pub struct BsIntermediates {
pub d1: i128,
pub d2: i128,
pub phi_d1: i128, pub phi_d2: i128, pub phi_neg_d1: i128, pub phi_neg_d2: i128, pub pdf_d1: i128, pub discount: i128, pub sqrt_t: i128, pub sigma_sqrt_t: i128, pub k_disc: i128, }
pub struct BsFull {
pub call: u128,
pub put: u128,
pub call_delta: i128,
pub put_delta: i128,
pub gamma: i128,
pub vega: i128,
pub call_theta: i128,
pub put_theta: i128,
pub call_rho: i128,
pub put_rho: i128,
}
pub const SQRT_2PI_IV: i128 = 2_506_628_274_631; pub const PADE_P0: i128 = 1_030_712_890_981;
pub const PADE_P1: i128 = -3_523_716_038_286;
pub const PADE_P2: i128 = 2_286_295_838_170;
pub const PADE_P3: i128 = 148_646_789_634;
pub const PADE_P4: i128 = 72_825_165_192;
pub const PADE_Q1: i128 = -2_282_582_703_163;
pub const PADE_Q2: i128 = -135_934_203_446;
pub const PADE_Q3: i128 = -77_424_332_735;
pub const PADE_Q4: i128 = 446_572_216;
pub const LI_P1: i128 = -969_271_876_255; pub const LI_P2: i128 = 97_428_338_274; pub const LI_P3: i128 = 1_750_081_126_685;
pub const LI_N: [i128; 14] = [
-68_098_378_725, 440_639_436_211, -263_473_754_689, -5_792_537_721_792, -5_267_481_008_429, 4_714_393_825_758, 3_529_944_137_559, -23_636_495_876_611, -9_020_361_771_283, 14_749_084_301_452, -32_570_660_102_526, 76_398_155_779_133, 41_855_161_781_749, -12_150_611_865_704, ];
pub const LI_M: [i128; 14] = [
6_268_456_292_246, -6_284_840_445_036, 30_068_281_276_567, -11_780_036_995_036, -2_310_966_989_723, -11_473_184_324_152, -230_101_682_610_568, 86_127_219_899_668, 3_730_181_294_225, -13_954_993_561_151, 261_950_288_864_225, 20_090_690_444_187, -50_117_067_019_539, 13_723_711_519_422, ];
pub const SQRT_ONE_OVER_THREE: i128 = 577_350_269_190;
pub const TWO_PI_OVER_SQRT_TWENTY_SEVEN: i128 = 1_209_199_576_156;
pub const PI_OVER_SIX: i128 = 523_598_775_598;
pub const SQRT_PI_OVER_TWO: i128 = 1_253_314_137_316;
pub const TWO_PI_SCALED: i128 = 6_283_185_307_180;
pub const SQRT_THREE_SCALED: i128 = 1_732_050_807_569;
pub const AS241_SPLIT1: i128 = 425_000_000_000; pub const AS241_CONST1: i128 = 180_625_000_000; pub const AS241_SPLIT2: i128 = 5_000_000_000_000; pub const AS241_CONST2: i128 = 1_600_000_000_000;
pub const AS241_A: [i128; 8] = [
3_387_132_872_796, 133_141_667_891_784, 1_971_590_950_306_551, 13_731_693_765_509_461, 45_921_953_931_549_871, 67_265_770_927_008_701, 33_430_575_583_588_128, 2_509_080_928_730_123, ];
pub const AS241_B: [i128; 7] = [
42_313_330_701_601, 687_187_007_492_058, 5_394_196_021_424_751, 21_213_794_301_586_596, 39_307_895_800_092_711, 28_729_085_735_721_943, 5_226_495_278_852_855, ];
pub const AS241_C: [i128; 8] = [
1_423_437_110_750, 4_630_337_846_157, 5_769_497_221_461, 3_647_848_324_763, 1_270_458_252_452, 241_780_725_177, 22_723_844_989, 774_545_014, ];
pub const AS241_D: [i128; 7] = [
2_053_191_626_638, 1_676_384_830_184, 689_767_334_985, 148_103_976_427, 15_198_666_564, 547_593_808, 1_051, ];
pub const AS241_E: [i128; 8] = [
6_657_904_643_501, 5_463_784_911_164, 1_784_826_539_917, 296_560_571_829, 26_532_189_527, 1_242_660_947, 27_115_556, 201_033, ];
pub const AS241_F: [i128; 7] = [
599_832_206_556, 136_929_880_923, 14_875_361_291, 786_869_131, 18_463_183, 142_151, 0, ];
pub const AS241_SPLIT_2B: i128 = 3_000_000_000_000; pub const AS241_CENTER_2B: i128 = 3_500_000_000_000; pub const AS241_SPLIT_2C: i128 = 4_000_000_000_000; pub const AS241_CENTER_2C: i128 = 4_500_000_000_000;
pub const AS241_G: [i128; 8] = [
4_426_662_374_924,
88_005_498_751,
-41_160_164_851,
39_126_451_267,
-58_494_244_713,
66_910_003_962,
33_959_185_442,
3_528_770_658,
];
pub const AS241_H: [i128; 7] = [
-321_373_526_953,
105_028_553_616,
-29_555_849_092,
-2_051_462_415,
15_427_578_835,
2_488_089_624,
293_654,
];
pub const AS241_I: [i128; 8] = [
5_920_458_342_163,
44_499_785_186,
-13_437_857_419,
10_089_893_956,
-18_863_036_526,
29_961_578_652,
10_532_292_799,
830_578_919,
];
pub const AS241_J: [i128; 7] = [
-242_472_999_986,
60_245_461_302,
-14_160_150_896,
615_344_034,
4_831_648_864,
586_268_402,
34_994,
];
pub const LN_TABLE_STEP: u128 = SCALE / 16;
pub const LN_TABLE_HALF_STEP: u128 = SCALE / 32;
pub const LN_TABLE_HP_STEP: u128 = SCALE_HP_U / 16;
pub const LN_TABLE_HP_HALF_STEP: u128 = SCALE_HP_U / 32;
pub const LN_TABLE_16: [i128; 16] = [
30771658667, 89612158690, 145182009844, 197825743330, 247836163905, 295464212894, 340926586971, 384411698910, 426084395311, 466089729925, 504556010752, 541597282433, 577315365035, 611801541106, 645137961374, 677398823592, ];
pub const LN_TABLE_LO_16: [i128; 16] = [
-246311628972, -312867380049, 497897281935, -80119637428, -418743219397, -164123613318, -406789694911, 332039734790, -99936875455, -400775441381, 395287058309, -255628423458, -176395681888, -7096470110, -415298334772, -193859190317, ];
pub const LN_TABLE_HP_16: [i128; 16] = [
30771658666754, 89612158689687, 145182009844498, 197825743329920, 247836163904581, 295464212893836, 340926586970593, 384411698910332, 426084395310900, 466089729924599, 504556010752395, 541597282432744, 577315365034824, 611801541105993, 645137961373585, 677398823591806, ];
pub const LN_TABLE_HP_LO_16: [i128; 16] = [
-311628971792403, 132619951469378, -102718064936259, -119637427928803, 256780602765747, -123613318093945, 210305089199780, 39734790062481, 63124544879595, 224558619247505, 287058308531738, 371576542303900, -395681887938481, -96470110233571, -298334771503865, 140809682609997, ];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ln2_lo_sub_ulp() {
assert!(
(LN2_LO as u128) < SCALE || ((-LN2_LO) as u128) < SCALE,
"LN2_LO={} not sub-ULP",
LN2_LO
);
}
#[test]
fn test_ln2_hp_lo_sub_ulp() {
assert!(
(LN2_HP_LO.unsigned_abs()) < SCALE_HP_U,
"LN2_HP_LO={} not sub-ULP",
LN2_HP_LO
);
}
#[test]
fn test_ln2_lo_no_overflow() {
for k in -127i128..=127 {
assert!(k.checked_mul(LN2_LO).is_some(), "overflow at k={}", k);
}
}
#[test]
fn test_ln2_hp_lo_no_overflow() {
for k in -127i128..=127 {
assert!(k.checked_mul(LN2_HP_LO).is_some(), "overflow at k={}", k);
}
}
#[test]
fn test_ln2_correction_bounded() {
for k in -88i128..=88 {
let raw = k * LN2_LO;
let correction = if raw >= 0 {
(raw + SCALE_I / 2) / SCALE_I
} else {
(raw - SCALE_I / 2) / SCALE_I
};
assert!(
correction.abs() <= 10,
"Correction too large at k={}: {}",
k,
correction
);
}
}
#[test]
fn test_ln_remez_coeffs_matches_individual() {
assert_eq!(LN_REMEZ_COEFFS[0], LN_REMEZ_W0);
assert_eq!(LN_REMEZ_COEFFS[1], LN_REMEZ_W1);
assert_eq!(LN_REMEZ_COEFFS[7], LN_REMEZ_W7);
}
#[test]
fn test_ln_remez_hp_coeffs_matches_individual() {
assert_eq!(LN_REMEZ_HP_COEFFS[0], LN_REMEZ_HP0);
assert_eq!(LN_REMEZ_HP_COEFFS[9], LN_REMEZ_HP9);
}
#[test]
fn test_ln_table_lo_sub_ulp() {
for j in 0..16 {
assert!(
LN_TABLE_LO_16[j].unsigned_abs() < SCALE,
"LN_TABLE_LO_16[{}]={} not sub-ULP",
j,
LN_TABLE_LO_16[j]
);
}
}
#[test]
fn test_ln_table_hp_lo_sub_ulp() {
for j in 0..16 {
assert!(
LN_TABLE_HP_LO_16[j].unsigned_abs() < SCALE_HP_U,
"LN_TABLE_HP_LO_16[{}]={} not sub-ULP",
j,
LN_TABLE_HP_LO_16[j]
);
}
}
#[test]
fn test_ln_table_monotone() {
for j in 1..16 {
assert!(
LN_TABLE_16[j] > LN_TABLE_16[j - 1],
"LN_TABLE_16 not monotone at j={}",
j
);
assert!(
LN_TABLE_HP_16[j] > LN_TABLE_HP_16[j - 1],
"LN_TABLE_HP_16 not monotone at j={}",
j
);
}
}
}