#![warn(missing_docs)]
pub mod backend;
pub mod cache;
pub mod compute;
pub mod constants;
pub mod series;
pub use compute::digits_to_bits as bits_for_digits;
pub use constants::{NI_50_DIGITS, NI_F32, NI_F64};
#[cfg(all(feature = "backend-rug", not(feature = "backend-dashu")))]
type ActiveBackend = backend::rug_backend::RugBackend;
#[cfg(feature = "backend-dashu")]
type ActiveBackend = backend::dashu::DashuBackend;
#[cfg(all(
feature = "backend-f64",
not(feature = "backend-dashu"),
not(feature = "backend-rug")
))]
type ActiveBackend = backend::f64_backend::F64Backend;
use cache::NiCache;
static CACHE: NiCache<ActiveBackend> = NiCache::new();
pub fn ni_number_digits(decimal_digits: u32) -> String {
let bits = compute::digits_to_bits(decimal_digits);
let val = CACHE.get_or_compute(bits);
use backend::NiFloat;
val.to_decimal_string(decimal_digits)
}
pub fn ni_number(precision_bits: u32) -> <ActiveBackend as backend::NiBackend>::Float {
CACHE.get_or_compute(precision_bits)
}
pub fn ni_series(precision_bits: u32) -> series::NiSeries<ActiveBackend> {
series::NiSeries::new(precision_bits)
}
pub fn clear_cache() {
CACHE.clear();
}
#[cfg(test)]
mod tests {
use super::*;
use backend::NiFloat;
use serial_test::serial;
#[test]
#[serial]
fn digits_prefix_is_correct() {
let s = ni_number_digits(20);
assert!(s.starts_with("1.88937666040491913"), "wrong prefix: {}", s);
}
#[test]
#[serial]
fn f64_matches_precomputed_constant() {
let computed = ni_number(128).to_f64();
assert!(
(computed - NI_F64).abs() < 1e-13,
"drift: {:.2e}",
(computed - NI_F64).abs()
);
}
#[test]
#[serial]
fn cache_is_idempotent() {
let a = ni_number(128).to_f64();
let b = ni_number(128).to_f64();
assert_eq!(a, b, "cache returned different values");
}
#[test]
#[serial]
fn series_converges_to_constant() {
use crate::series::NiSeries;
let sum = NiSeries::<ActiveBackend>::new(256)
.take(20)
.last()
.unwrap()
.sum
.to_f64();
assert!(
(sum - NI_F64).abs() < 1e-13,
"series drift: {:.2e}",
(sum - NI_F64).abs()
);
}
#[test]
#[serial]
fn clear_and_recompute_is_stable() {
let _ = ni_number(64);
clear_cache();
let val = ni_number(64).to_f64();
assert!((val - NI_F64).abs() < 1e-12);
}
mod edge_cases {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn test_zero_digits() {
let s = ni_number_digits(0);
assert_eq!(s, "2", "0 decimal digits should round to 2");
}
#[test]
#[serial]
fn test_massive_cache_jump() {
clear_cache();
let _ = ni_number_digits(10);
let high = ni_number_digits(1000);
assert!(high.starts_with("1.88937666040491913115597775087642096081019761538215"));
}
}
}