use crate::core_type::I128;
const SCALE_REF: u32 = 35;
const PI_RAW_S35: i128 = 314_159_265_358_979_323_846_264_338_327_950_288_i128;
const TAU_RAW_S35: i128 = 628_318_530_717_958_647_692_528_676_655_900_577_i128;
const HALF_PI_RAW_S35: i128 = 157_079_632_679_489_661_923_132_169_163_975_144_i128;
const QUARTER_PI_RAW_S35: i128 = 78_539_816_339_744_830_961_566_084_581_987_572_i128;
const E_RAW_S35: i128 = 271_828_182_845_904_523_536_028_747_135_266_250_i128;
const GOLDEN_RAW_S35: i128 = 161_803_398_874_989_484_820_458_683_436_563_812_i128;
#[inline]
fn rescale_from_ref(raw: i128, target_scale: u32) -> i128 {
if target_scale == SCALE_REF {
return raw;
}
if target_scale < SCALE_REF {
let shift = SCALE_REF - target_scale;
let divisor = 10i128.pow(shift);
let half = divisor / 2;
let rounded = if raw >= 0 { raw + half } else { raw - half };
rounded / divisor
} else {
let shift = target_scale - SCALE_REF;
raw * 10i128.pow(shift)
}
}
pub trait DecimalConsts: Sized {
fn pi() -> Self;
fn tau() -> Self;
fn half_pi() -> Self;
fn quarter_pi() -> Self;
fn golden() -> Self;
fn e() -> Self;
}
impl<const SCALE: u32> DecimalConsts for I128<SCALE> {
#[inline]
fn pi() -> Self {
Self(rescale_from_ref(PI_RAW_S35, SCALE))
}
#[inline]
fn tau() -> Self {
Self(rescale_from_ref(TAU_RAW_S35, SCALE))
}
#[inline]
fn half_pi() -> Self {
Self(rescale_from_ref(HALF_PI_RAW_S35, SCALE))
}
#[inline]
fn quarter_pi() -> Self {
Self(rescale_from_ref(QUARTER_PI_RAW_S35, SCALE))
}
#[inline]
fn golden() -> Self {
Self(rescale_from_ref(GOLDEN_RAW_S35, SCALE))
}
#[inline]
fn e() -> Self {
Self(rescale_from_ref(E_RAW_S35, SCALE))
}
}
impl<const SCALE: u32> I128<SCALE> {
pub const EPSILON: Self = Self(1);
pub const MIN_POSITIVE: Self = Self(1);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core_type::I128s12;
#[test]
fn pi_is_bit_exact_at_scale_12() {
assert_eq!(I128s12::pi().to_bits(), 3_141_592_653_590_i128);
}
#[test]
fn tau_is_bit_exact_at_scale_12() {
assert_eq!(I128s12::tau().to_bits(), 6_283_185_307_180_i128);
}
#[test]
fn half_pi_is_bit_exact_at_scale_12() {
assert_eq!(I128s12::half_pi().to_bits(), 1_570_796_326_795_i128);
}
#[test]
fn quarter_pi_is_bit_exact_at_scale_12() {
assert_eq!(I128s12::quarter_pi().to_bits(), 785_398_163_397_i128);
}
#[test]
fn e_is_bit_exact_at_scale_12() {
assert_eq!(I128s12::e().to_bits(), 2_718_281_828_459_i128);
}
#[test]
fn golden_is_bit_exact_at_scale_12() {
assert_eq!(I128s12::golden().to_bits(), 1_618_033_988_750_i128);
}
#[test]
fn pi_close_to_f64_pi() {
let diff = (I128s12::pi().to_f64_lossy() - core::f64::consts::PI).abs();
assert!(diff < 1e-11, "pi diverges from f64 PI by {diff}");
}
#[test]
fn tau_close_to_f64_tau() {
let diff = (I128s12::tau().to_f64_lossy() - core::f64::consts::TAU).abs();
assert!(diff < 1e-11, "tau diverges from f64 TAU by {diff}");
}
#[test]
fn half_pi_close_to_f64_frac_pi_2() {
let diff =
(I128s12::half_pi().to_f64_lossy() - core::f64::consts::FRAC_PI_2).abs();
assert!(diff < 1e-11, "half_pi diverges from f64 FRAC_PI_2 by {diff}");
}
#[test]
fn quarter_pi_close_to_f64_frac_pi_4() {
let diff =
(I128s12::quarter_pi().to_f64_lossy() - core::f64::consts::FRAC_PI_4).abs();
assert!(
diff < 1e-11,
"quarter_pi diverges from f64 FRAC_PI_4 by {diff}"
);
}
#[test]
fn e_close_to_f64_e() {
let diff = (I128s12::e().to_f64_lossy() - core::f64::consts::E).abs();
assert!(diff < 1e-11, "e diverges from f64 E by {diff}");
}
#[cfg(feature = "std")]
#[test]
fn golden_close_to_closed_form() {
let expected = (1.0_f64 + 5.0_f64.sqrt()) / 2.0;
let diff = (I128s12::golden().to_f64_lossy() - expected).abs();
assert!(diff < 1e-11, "golden diverges from closed-form by {diff}");
}
#[test]
fn epsilon_is_one_ulp() {
assert_eq!(I128s12::EPSILON.to_bits(), 1_i128);
assert!(I128s12::EPSILON > I128s12::ZERO);
}
#[test]
fn min_positive_is_one_ulp() {
assert_eq!(I128s12::MIN_POSITIVE.to_bits(), 1_i128);
assert_eq!(I128s12::MIN_POSITIVE, I128s12::EPSILON);
}
#[test]
fn epsilon_at_scale_6_is_one_ulp() {
type D6 = I128<6>;
assert_eq!(D6::EPSILON.to_bits(), 1_i128);
assert_eq!(D6::MIN_POSITIVE.to_bits(), 1_i128);
}
#[test]
fn pi_at_scale_6_is_bit_exact() {
type D6 = I128<6>;
assert_eq!(D6::pi().to_bits(), 3_141_593_i128);
}
#[test]
fn pi_at_scale_0_is_three() {
type D0 = I128<0>;
assert_eq!(D0::pi().to_bits(), 3_i128);
}
#[test]
fn pi_at_scale_ref_is_raw_constant() {
type D35 = I128<35>;
assert_eq!(D35::pi().to_bits(), PI_RAW_S35);
}
#[test]
fn pi_at_scale_36_multiplies_raw_by_ten() {
type D36 = I128<36>;
assert_eq!(D36::pi().to_bits(), PI_RAW_S35 * 10);
}
#[test]
fn neg_pi_round_trip() {
let pi = I128s12::pi();
let neg_pi = -pi;
assert_eq!(neg_pi.to_bits(), -3_141_592_653_590_i128);
}
}