Shared Rust kernels
ported/combin
All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.
combin_kernel · 65 lines · calc-func/src/ported/combin.rs on GitHub/// Worksheet `COMBIN` on the current x64 Excel reference build.
///
/// W109 identified a cyclic rising-factor graph rather than the conventional
/// multiply-first recurrence. After `k = min(k, n-k)`, Excel evaluates
///
/// `product(i=2..=k, (n-k+i-1)/i) * n`
///
/// in ascending `i`, storing every quotient and accumulator product from x87
/// PC64 to binary64 (`RN53(RN64(op))`), then multiplying `n` last through the
/// same stored-x87 operation. This graph is exact on 505 legacy rows, 20,713
/// current-build discovery rows, and a frozen 1,024-row publication heldout.
pub fn combin_kernel(n: f64, k: f64) -> Result<f64, WorksheetErrorCode> {
use crate::ported::excel_numeric::{excel_x87_div, excel_x87_mul};
// Range.Value2 cannot inject NaN/infinity into the live worksheet probe,
// but the direct Rust kernel must not inherit saturating float-to-int casts.
if !n.is_finite() || !k.is_finite() {
return Err(WorksheetErrorCode::Num);
}
// The current x64 reference uses denormals-are-zero at this admission
// seam. Preserve the sign on the zero itself; ordinary `< 0` checks then
// admit either subnormal sign as zero while rejecting negative normals.
let n = if n.is_subnormal() {
0.0_f64.copysign(n)
} else {
n
};
let k = if k.is_subnormal() {
0.0_f64.copysign(k)
} else {
k
};
let n = trunc_nonnegative(n)?;
let raw_k = trunc_nonnegative(k)?;
if n > COMBIN_MAX_N {
return Err(WorksheetErrorCode::Num);
}
if raw_k > n {
return Err(WorksheetErrorCode::Num);
}
let k = raw_k.min(n - raw_k);
if k == 0 {
return Ok(1.0);
}
let mut acc = 1.0;
for i in 2..=k {
let numerator = (n - k + i - 1) as f64;
let factor = excel_x87_div(numerator, i as f64);
acc = excel_x87_mul(acc, factor);
// With k reduced to min(k,n-k), every remaining factor is greater
// than one. Once the accumulator becomes nonfinite it cannot recover;
// short-circuiting preserves the typed #NUM result and prevents an
// admitted near-central 32-bit input from walking ~1e9 iterations.
if !acc.is_finite() {
return Err(WorksheetErrorCode::Num);
}
}
let result = excel_x87_mul(acc, n as f64);
if result.is_finite() {
Ok(result)
} else {
Err(WorksheetErrorCode::Num)
}
}
Used by 2 functions.