Commit Graph
135 Commits
Author SHA1 Message Date
Brandon Thomas c305315610 Merge PR #2719 2026-10-30 15:42:10 -04:00
Dylan M. Taylor 7898cc8cfd Refactor functions for strict Clippy checks 2026-10-28 10:52:53 +00:00
Higgs32584andClaude Opus 5.5 7d828c01f7 Security: cap the work of lifted lookups, needles x lookup vector, with #NUM! (#2716)
A lookup lifted over an array of needles searched its whole lookup vector once per needle
with no total cap, so =ROWS(XMATCH(SEQUENCE(1048576),-SEQUENCE(1048576))) in a 1.6 KB file
ran for hours and hung file.open. VLOOKUP, HLOOKUP, LOOKUP, MATCH, XLOOKUP and XMATCH now
return #NUM! past 5e8 needle x entry steps, the pattern MMULT uses. Other lifted functions
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 00:40:18 -04:00
aiongg 1db55e4e5f refactor(functions): move math.rs items to math/ 2026-10-26 20:43:39 +00:00
aiongg be18ee0519 refactor(functions): move engineering.rs items to engineering/ 2026-10-26 20:43:39 +00:00
aiongg 17b29b891d refactor(functions): move financial.rs items to financial/ 2026-10-26 20:43:39 +00:00
aiongg 1ff44e5ba2 refactor(functions): move stat.rs items to stat/ 2026-10-26 20:43:39 +00:00
aiongg a8db625e6b style: cargo fmt on the moved test files 2026-10-26 18:15:59 +00:00
aiongg acade653ba refactor(functions): move stat tests to src/stat/tests.rs 2026-10-26 18:15:44 +00:00
aiongg b3711f59ce refactor(functions): move math tests to src/math/tests.rs 2026-10-26 18:15:43 +00:00
aiongg 341ae6848e refactor(functions): move financial tests to src/financial/tests.rs 2026-10-26 18:15:43 +00:00
3b65b9af58 Sonar maintainability: explicit imports, method paths, smaller functions (#2412)
* Sonar: explicit imports, fn paths for closures, extract helpers for cognitive complexity

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Fix imports

* Fix serde imports

* Fix engine imports

* Fix clippy gate and a type error from the explicit-import change

Remove the unused imports the review flagged (cf.rs, pivot.rs, tables.rs, cmd/sheet.rs) so clippy -D warnings passes, and point the backstage parent-folder lookup at Path::to_str, which the fn-path rewrite had broken.

Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Apply rustfmt to the explicit-import cleanup so the fmt gate passes

* Share the per-directory font path expansion in theme.rs

The candidate font lists repeated the same loop over the Linux font
directories in every role. A single helper expands them in the same order,
so the lookup order is unchanged and the duplicated blocks go away.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Read the Windows fonts folder in one place in theme.rs

Every script-font list repeated the same WINDIR lookup. A single helper
returns the same path, so the duplicated lines go away without changing
which font files are tried.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Build the light and dark UI palettes from one table in theme.rs

Tokens::light() and Tokens::dark() listed the same 40 colour fields in the
same order, so every change had to be made twice. A single palette
constructor takes the mode and picks each colour's value, so each field is
written once and both modes keep their exact colours.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Share the Noto CJK font list between the CJK script lists in theme.rs

The Japanese, Simplified and Korean lists each repeated the same loop over
the Linux CJK directories with a different locale file name. One helper
builds that loop, and each list keeps its own face index, so the lookup
order is unchanged.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* Share Linux Noto directory list across Thai and Hebrew font lookups

Removes the duplicated directory arrays flagged by SonarCloud new-code duplication.

* Reuse in_dirs for Thai and Hebrew Noto font candidates

Removes the remaining duplicated per-directory loops flagged by SonarCloud.

* Share one font-list builder for the Thai and Hebrew faces

The two per-script functions repeated the same Windows, macOS, Linux and
variable-font setup. A single script_fonts helper now builds both lists,
keeping the candidate order unchanged, which clears the Sonar duplication gate.

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-25 12:32:02 -04:00
Brandon Thomas 6208f15d29 Merge pull request #2463 from Silvertree2010:fix/h2062-yearfrac-basis-0-counts-the-last 2026-10-25 09:36:28 -04:00
Brandon Thomas 25d6a3a44f Merge pull request #2457 from Silvertree2010:fix/h1979-imsqrt-of-a-negative-real-return 2026-10-25 09:36:27 -04:00
Brandon Thomas 1871541948 Merge pull request #2443 from Silvertree2010:fix/h2018-isoweeknum-weeknum-21-return-wee 2026-10-25 09:36:26 -04:00
Brandon Thomas 990aa1315a Merge pull request #2428 from Silvertree2010:fix/h1934-minverse-returns-0-for-inverse-e 2026-10-25 09:36:25 -04:00
NetterSilverandClaude Haiku 5.5 b4487663aa Fix YEARFRAC basis 0 for February-end start and 31st end
Found while testing crates/functions/src/date.rs:443-457 (yearfrac_core, basis 0)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 17:10:13 +02:00
NetterSilverandClaude Haiku 5.5 92d2b9894f Fix IMSQRT leaving a stray real part for negative reals
Found while testing crates/functions/src/engineering.rs:713 (imsqrt, via Cx::powf at :562)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 14:24:48 +00:00
NetterSilverandClaude Haiku 5.5 86064077f1 Fix ISOWEEKNUM/WEEKNUM(…,21) for the first days of the calendar
Found while testing crates/functions/src/date.rs:123-129 (isoweek)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 15:04:35 +02:00
NetterSilverandClaude Haiku 5.5 6cb3220164 Scale MINVERSE rounding cutoff to the input matrix
Found while testing crates/functions/src/math.rs:923

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 11:09:26 +00:00
NetterSilverandClaude Haiku 5.5 14415f410d Fix PERCENTRANK returning #NUM! for very large significance
Found while testing crates/functions/src/stat.rs:257-260 (trunc_digits, used by percentrank_impl)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 11:16:03 +02:00
Dylan M. TaylorandCodebuff f284f6a825 M2.9: budget lambda work and bound wildcard substring search (#2415)
Sweep findings from the ac8da7d4 security scan (storytold-gridcraft-agent-
findings.json), remaining on main at 72c1d57d:

- finding 14 (crates/calc/src/eval.rs:988): every lambda invocation now
  spends one charge against a per-cell work budget (16M — one per cell of
  the largest array a reference may expand to), so a hostile, deeply
  nested LAMBDA/LET formula stops at the bound a legitimately wide one
  could reach instead of growing exponentially with nesting. Exhaustion
  is #CALC!, like the existing depth cap. Metered at `call_lambda_values`
  so every path into a body is counted (direct application, MAP/REDUCE/
  SCAN/BYROW/BYCOL/MAKEARRAY, D-function criteria); the criteria
  sub-evaluator hands its spend back to the parent.
- finding 16 (crates/functions/src/criteria.rs:313): `match_from` takes a
  step budget (1<<26: an honest match against Excel's 32,767-char cell
  ceiling costs a few hundred thousand steps), and `wildcard_find` drops
  the leading star run (a no-op for a prefix match) so the first literal
  anchor skips straight to its occurrences; all suffix attempts share one
  budget. Star churn degrades to no-match instead of quadratic work.

Unit tests: mock-host exhaustion/under-budget for lambda applications
(eval::fuel_tests), nested REDUCE under budget in full recalc, wildcard
behaviour unchanged.

🤖 Generated with Codebuff

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-24 05:07:08 -04:00
Brandon ThomasandClaude Opus 5.5 72c1d57d1e Review fixes for #2393, #2394, #2397 and #2399
- PMT: take rate / m first so (pv + fv) / m can't overflow for a tiny rate (PMT(1e-300,10,1e12)).
- Keytips probe: an empty params object keys like no params.
- Number Stored as Text check reads the same accounting parentheses as typed entry.
- Lookups: a NaN number falls back to the general comparison instead of comparing equal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 02:12:07 -04:00
Brandon Thomas 2c6e529b4d Merge PR #2400 2026-10-24 02:10:42 -04:00
Brandon Thomas d743f21992 Merge PR #2399
# Conflicts:
#	ROADMAP.md
#	docs/function-parity.md
2026-10-24 02:10:42 -04:00
Brandon Thomas f9af0d88a5 Merge PR #2397
# Conflicts:
#	ROADMAP.md
#	docs/function-parity.md
2026-10-24 02:10:42 -04:00
Brandon Thomas 14d6dabcaf Merge PR #2396 2026-10-24 02:10:36 -04:00
NetterSilverandClaude Haiku 5.5 b47c83f6aa Reject unmatched '.' and ',' in NUMBERVALUE text
Found while testing crates/functions/src/text.rs:326-340

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-24 01:48:12 +00:00
Harrison Booth 35067fa9ca Read accounting negatives like $(1,234) in number text
Typed entry, VALUE and arithmetic now read $(1,234), $ (5) and (5)% as
negative numbers, as Excel 365 does. Parentheses no longer nest: (-5),
((5)), $5% and dates in parentheses stay text. This changes main's
((100)) from 100 to text; the no-recursion guard from #2180 is kept and
its test now asserts the new result.
2026-10-23 16:57:24 -05:00
Harrison Booth 86f4114514 Merge remote-tracking branch 'origin/main' into gc-lookup-exact-number-compare
# Conflicts:
#	ROADMAP.md
#	docs/function-parity.md
2026-10-23 16:55:41 -05:00
Harrison Booth e883c84e7b Merge origin/main into the Analysis ToolPak value-argument branch
Resolves conflicts in ROADMAP.md, docs/function-parity.md and
crates/calc/src/tests.rs: both sides' progress-log rows and tests are kept.
2026-10-23 16:55:37 -05:00
Harrison Booth 1ce4781d3e Accuracy: PMT to Excel's last bits via ln_1p/exp_m1
PMT computed (1 + rate)^nper with powf, which rounds 1 + rate first and
drops the low bits of a small rate. Over 400 synthetic probes that left
PMT up to about 1000 ULPs from Excel (59 bit-exact); the ln_1p/exp_m1 form
is within 4 ULPs (244 bit-exact). A loan schedule guarded by
IF(p - SUM(principal) > 0, ...) turns on those bits: in 3000 synthetic
schedules main disagreed with Excel on the number of live periods in 1382,
the fix in 823.

Also follows Excel at the edges: a rate of -1 or less is #NUM!, a huge
nper gives -pv*rate instead of #NUM!, a tiny rate no longer collapses, and
a small negative rate keeps its payment. IPMT, PPMT, CUMIPMT and CUMPRINC
pick this up through pmt_calc. FV and PV are unchanged: Excel's own FV/PV
sit further from the exact value than the current powf form does.

The tests allow the one-ULP libm difference between platforms.
2026-10-23 16:24:18 -05:00
Brandon ThomasandClaude Opus 5.5 7cf44d0fbb Integration and review fixes for #2367, #2369 and #2387
- #2369: its test joins the single ui-egui integration-test binary (tests/it), as main now builds them.
- #2367: a cell set to empty text is a blank entry and doesn't grow the AutoFilter (CodeRabbit).
- #2387: MOD's range check also covers the sign-corrected remainder (CodeRabbit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:05:43 -04:00
NetterSilverandClaude Haiku 5.5 c746eea876 Fix MOD returning wrong sign and magnitude for tiny divisors
Found while testing crates/functions/src/math.rs:305-319

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-23 21:43:22 +02:00
Brandon Thomas d1399ff2ca Merge PR #2279 2026-10-23 10:11:12 -04:00
NetterSilverandClaude Opus 5.5 8307d3cb05 Treat empty instance_num in TEXTBEFORE/TEXTAFTER as 1
Found while testing crates/functions/src/text.rs:553

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:25:54 +00:00
Tyrel HavemanandClaude Opus 5.5 742a6e8535 functions: REGEXREPLACE bounds each replacement as it expands it (#2162)
From the local CodeRabbit review: the result was bounded per match, but
one match's expansion was built whole first, so a replacement repeating
$0 10,000 times over a 32,767-character match allocated about 330 MB
before the check. The replacement is now split into pieces as regex's
Captures::expand reads it (template_expands_like_regex checks they agree)
and appended piece by piece within the bound: 327 MB peak became 7 MB
(regexreplace_bounds_its_result).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP
2026-10-21 23:45:50 -07:00
Tyrel HavemanandClaude Opus 5.5 e177bf1c44 calc: hardening: overlong digits, NaN indexes, REGEXREPLACE output (#2163, #2162)
- Text coercion: digits too many for a finite number are not a number,
  and a fraction must come out finite, so "1 <400 digits>/<400 digits>"
  (inf/inf) no longer coerces to NaN (test overlong_digits_stay_text).
- VLOOKUP/HLOOKUP and the D-functions' field index refuse NaN, which
  passed their "< 1" checks and became index 0, then underflowed
  (test nan_index_is_a_value_error; hostile_inputs_never_panic now also
  passes NaN and infinity to every function).
- REGEXREPLACE replaces match by match and gives up once the result can
  only be too long, instead of building every replacement first: the
  32,767 x 32,767 worst case went from 1.05 GB peak to 7 MB
  (test regexreplace_bounds_its_result).

#2165 (MAP's size check) is already fixed on main by the broadcast cap
(836b2698, #2182), whose test covers MAP.

Also adds my contributors/people.toml entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP
2026-10-21 23:41:28 -07:00
aiongg a680ac59f0 Integration: track t07
# Conflicts:
#	crates/calc/src/tests.rs
#	docs/function-parity.md
2026-10-22 03:03:23 +00:00
aiongg 15dfbef548 Merge PR #2195: calc: cap LINEST-family regressor matrices (#2177) 2026-10-22 02:35:10 +00:00
aiongg 0cc4798ca9 Merge PR #1650: fix(ets): avoid repeated scans while interpolating gaps
docs/function-parity.md: status line takes this PR's change text with the newer review date; both history rows kept, newest first.
2026-10-22 02:34:55 +00:00
aiongg 2a3ea02e69 Merge PR #1526: functions: NETWORKDAYS.INTL accepts the all-weekend string "1111111" 2026-10-22 02:34:46 +00:00
aiongg 806a439efa Merge PR #1507: Read securities function dates in the workbook's date system 2026-10-22 02:34:46 +00:00
aiongg 011ae7266d Merge PR #1469: Let GROUPBY and PIVOTBY take a LAMBDA as their function 2026-10-22 02:34:45 +00:00
aiongg 941da33355 Merge PR #1432: Stop BINOM.INV/CRITBINOM hanging when trials exceed 2^53 2026-10-22 02:34:45 +00:00
aiongg a3baf552a1 Merge PR #1281: N() converts a computed array element by element, so it works inside SUMPRODUCT
crates/calc/src/tests.rs: kept both tests (union).
2026-10-22 02:34:42 +00:00
aiongg f55cbcd061 Merge PR #1237: BAHTTEXT: a final one after the millions reads เอ็ด 2026-10-22 02:34:35 +00:00
aiongg 91be133965 Merge PR #1114: Read date text arguments in the workbook date system (#1038) 2026-10-22 02:34:35 +00:00
aiongg a0a4b6a7d0 Merge PR #1105: Fix row keys merging distinct text rows in UNIQUE and grouping (#1012) 2026-10-22 02:34:35 +00:00
Higgs32584andClaude Opus 5.5 e1b8aad143 calc: cap LINEST-family regressor matrices (#2177)
LINEST, LOGEST, TREND and GROWTH built k x k normal-equation matrices
(plus two copies in sym_inverse, O(k^3)) with k taken straight from the
known_xs shape, so one formula could allocate gigabytes and abort on open.
More than 255 predictors is now #VALUE!, and more than 1e9 rows x k^2
multiply-adds is #NUM!, both checked before any allocation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:07:30 -04:00