# Verification log - BiostatKit (Phase A, B1, B2 and B3, 11 calculators)

Generated 2026-11-14 09:43 by `kiem/chay-kiem.py`. Reference: Python 3.14.3, SciPy 1.18.0, statsmodels 0.15.0, NumPy 2.5.1 (`kiem/tinh-chuan.py`, output `kiem/chuan.json`).
The JS under test is the page code itself (`window.__bk.compute` in Chrome headless), not the sample page in `01.Ban mau`.

**Result: 22/22 cases match** (334 values compared; pass = relative difference <= 5e-05, i.e. the same 4 significant digits; largest difference seen 4.2e-13).
**Negative controls** (a reference value changed on purpose: OR x 1.001, sample size + 0.5, and one value x 1.001 in each phase B case; plus a page left in the light theme must fail the dark-theme test, caught: True; plus a changed returned value must fail the behaviour check of ROC and of the index page, caught: 2/2): the comparison caught 12/12 of the phase B cases and both older ones - the check can fail.

Methods behind the reference values: OR Woolf CI (statsmodels Table2x2) + Pearson chi-square without correction (scipy chi2_contingency) + Fisher exact (scipy fisher_exact); RR Katz CI (checked with scipy relative_risk when no zero cell); ARR and NNT Newcombe hybrid score (statsmodels confint_proportions_2indep 'newcomb'); proportions Wilson (statsmodels proportion_confint); LR log-method (Simel, hand formula); sample size n = Z^2 p(1-p)/d^2 (checked with statsmodels samplesize_confint_proportion) and finite population correction.
Phase B1: two means = closed form, checked with statsmodels `normal_sample_size_one_tail`; two proportions = Fleiss formula with the variance pooled under H0, checked with statsmodels `samplesize_proportions_2indep_onetail` (equal groups also equal R `power.prop.test`); log-rank events = Schoenfeld and Freedman closed forms computed in Python with `scipy.stats.norm` (SciPy and statsmodels have no log-rank sample-size function, so the check is a round trip: the power implied by the events reproduces the target power to 1e-12); kappa, nominal, linear and quadratic, with its large-sample variance = statsmodels `cohens_kappa`. Expected CIs of the difference, of the hazard ratio and of kappa are hand formulas with `scipy.stats.norm`.
Phase B2: the six intraclass correlations use mean squares from statsmodels `anova_lm`, point estimates of Shrout and Fleiss (1979), exact F-pivot CIs for the one-way and mixed forms (round trip: the pivot returns 0.975 and 0.025 at the limits) and McGraw and Wong (1996) for the two-way random form, whose Satterthwaite degrees of freedom are computed in two algebraically equal forms and compared (the page uses the other form); the classic 6 x 4 example of Shrout and Fleiss gives the six values printed in the paper (0.17 0.29 0.71 0.44 0.62 0.91). Bland-Altman: bias CI equals the CI of `scipy.stats.ttest_1samp`, the trend is `scipy.stats.linregress`, and the CI of a limit of agreement (Bland and Altman 1999 variance) was checked by simulation in `tinh-chuan.py` (40000 samples of 30 pairs, coverage of the true limit 0.944).
Phase B3: the ROC curve and the AUC. The AUC equals the Mann-Whitney U statistic of `scipy.stats.mannwhitneyu` divided by n1 n0 and equals `sklearn.metrics.roc_auc_score` (sklearn 1.9.0); reversing the scale gives 1 - AUC. The DeLong variance is computed by two different algorithms (a brute-force one over every case/control pair, and the mid-rank one used by the page) that agree to 1e-12; the Hanley and McNeil (1982) standard error is a hand formula. The Youden cut-off is searched over every observed value (ties: the strictest cut-off), sensitivity, specificity and likelihood ratios at it follow the sensitivity-and-specificity case. The coverage of the DeLong 95 % interval was checked by simulation in `tinh-chuan.py` (20000 samples of 40 cases and 60 controls, coverage 0.938).

| case | tool | values | match |
|---|---|---|---|
| OR-1 | odds-ratio | 8 | yes |
| OR-2 | odds-ratio | 8 | yes |
| RR-1 | relative-risk | 14 | yes |
| RR-2 | relative-risk | 14 | yes |
| NNT-1 | nnt | 9 | yes |
| NNT-2 | nnt | 9 | yes |
| SS-1 | sensitivity-specificity | 25 | yes |
| SS-2 | sensitivity-specificity | 27 | yes |
| SZ-1 | sample-size-proportion | 6 | yes |
| SZ-2 | sample-size-proportion | 6 | yes |
| S2-1 | sample-size-two-groups | 13 | yes |
| S2-2 | sample-size-two-groups | 10 | yes |
| SV-1 | sample-size-survival | 13 | yes |
| SV-2 | sample-size-survival | 16 | yes |
| KP-1 | cohens-kappa | 13 | yes |
| KP-2 | cohens-kappa | 13 | yes |
| ICC-1 | icc | 24 | yes |
| ICC-2 | icc | 24 | yes |
| BA-1 | bland-altman | 14 | yes |
| BA-2 | bland-altman | 14 | yes |
| ROC-1 | roc-auc | 27 | yes |
| ROC-2 | roc-auc | 27 | yes |

## Page checks (local `python -m http.server`, Chrome headless)

| page | view | HTTP | console errors/warnings | failed or 4xx requests | external requests | horizontal overflow |
|---|---|---|---|---|---|---|
| index.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| odds-ratio.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| relative-risk.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| nnt.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| sensitivity-specificity.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| sample-size-proportion.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| sample-size-two-groups.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| sample-size-survival.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| cohens-kappa.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| icc.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| bland-altman.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| roc-auc.html | desktop 1280 | 200 | 0 | 0 | 0 | none (1265 <= 1280) |
| index.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| odds-ratio.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| relative-risk.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| nnt.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| sensitivity-specificity.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| sample-size-proportion.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| sample-size-two-groups.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| sample-size-survival.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| cohens-kappa.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| icc.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| bland-altman.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |
| roc-auc.html | mobile 390 | 200 | 0 | 0 | 0 | none (390 <= 390) |

Note: the browser on this PC also made 2 requests per page to `gc.kis.v2.scr.kaspersky-labs.com`; these are scripts injected by the local antivirus into every page served on localhost. They are not in the page source (a search of the page source for network calls finds none) and are excluded from the external-request column.

Dark theme (odds-ratio.html after clicking the toggle): data-theme=dark, page rgb(0, 0, 0), card (holds the buttons) rgb(29, 29, 31), primary button rgb(0, 102, 204); errors after toggle: 0.
Error path (empty cell, submit): message shown = "Please fill in every field. Exposed with outcome (a) is empty.".

Dark theme on the six phase B pages (localStorage cleared before each page, start theme light, then the toggle; the card holds the buttons and must be #1d1d1f = rgb(29, 29, 31)): sample-size-two-groups: start light -> dark, primary button rgb(0, 102, 204), card rgb(29, 29, 31), errors 0, ok; sample-size-survival: start light -> dark, primary button rgb(0, 102, 204), card rgb(29, 29, 31), errors 0, ok; cohens-kappa: start light -> dark, primary button rgb(0, 102, 204), card rgb(29, 29, 31), errors 0, ok; icc: start light -> dark, primary button rgb(0, 102, 204), card rgb(29, 29, 31), errors 0, ok; bland-altman: start light -> dark, primary button rgb(0, 102, 204), card rgb(29, 29, 31), errors 0, ok; roc-auc: start light -> dark, primary button rgb(0, 102, 204), card rgb(29, 29, 31), errors 0, ok.

Behaviour checks on the new pages:

| page | check | result |
|---|---|---|
| sample-size-two-groups | switch to two proportions: means fields hidden, proportion fields shown, 304 in total, R cross-check present | ok |
| sample-size-survival | hazard ratio 1 gives an error message, no exception | ok |
| cohens-kappa | category selector shows 25 / 9 / 4 cells for 5 / 3 / 2 categories; an empty cell gives an error message | ok |
| icc | default example 0.72 with 6 interval rows in the plot; ragged rows and text are refused with a message; model 3 + mean unit gives ICC(3,k) and an R script with qf() | ok |
| bland-altman | default example: 20 dots in the plot, bias -0.40; a line with one value and a zero-mean pair in percent mode are refused; percent mode labels the result % and the R script divides by the mean | ok |
| roc-auc | default example AUC 0.867 with 49 curve points and one cut-off dot, R script has no stray markup; a header line, a label 2, one group only and identical values are refused; perfect separation gives 1.000 with its note and the cut-off 7; direction 'lower' gives cut-off 3 or lower and flips x in R; the wrong direction gives 0.000 with a warning; two tied cut-offs show the strictest (8) with a note | ok |
| index | index shows 11 'Ready' cards (one is ROC curve and AUC, linked) and 4 'Coming soon', 15 in all | ok |
| cohens-kappa | 5 categories at 390 px: no horizontal overflow (scrollWidth 390) | ok |
| all 12 pages | every option of every select fits inside its box at 390 px (text width measured with the box's own font, 24 px kept for the arrow) | ok |

## Cases

### OR-1 - Template example, no zero cell

Tool: `odds-ratio` - input: `{"a": 20, "b": 80, "c": 10, "d": 90}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| zero | 0 | 0 | 0.0e+00 | yes |
| or | 2.25 | 2.25 | 0.0e+00 | yes |
| lo | 0.9942948923 | 0.9942948923 | 4.5e-16 | yes |
| hi | 5.091547829 | 5.091547829 | 5.2e-16 | yes |
| se | 0.4166666667 | 0.4166666667 | 0.0e+00 | yes |
| chi | 3.921568627 | 3.921568627 | 1.1e-16 | yes |
| pChi | 0.04767038066 | 0.04767038066 | 1.0e-15 | yes |
| pFisher | 0.07342758972 | 0.07342758972 | 4.2e-13 | yes |

### OR-2 - Zero cell (Haldane 0.5), small counts

Tool: `odds-ratio` - input: `{"a": 0, "b": 12, "c": 7, "d": 30}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| zero | 1 | 1 | 0.0e+00 | yes |
| or | 0.1626666667 | 0.1626666667 | 0.0e+00 | yes |
| lo | 0.008621792226 | 0.008621792226 | 1.8e-15 | yes |
| hi | 3.069019033 | 3.069019033 | 2.2e-15 | yes |
| se | 1.498706182 | 1.498706182 | 0.0e+00 | yes |
| chi | 2.648648649 | 2.648648649 | 0.0e+00 | yes |
| pChi | 0.103637964 | 0.103637964 | 2.4e-15 | yes |
| pFisher | 0.1711754835 | 0.1711754835 | 6.2e-15 | yes |

### RR-1 - Cohort 30/120 vs 18/130

Tool: `relative-risk` - input: `{"a": 30, "b": 90, "c": 18, "d": 112}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| zero | 0 | 0 | 0.0e+00 | yes |
| rr | 1.805555556 | 1.805555556 | 0.0e+00 | yes |
| rrLo | 1.063761771 | 1.063761771 | 4.2e-16 | yes |
| rrHi | 3.064624949 | 3.064624949 | 4.3e-16 | yes |
| arr | -0.1115384615 | -0.1115384615 | 0.0e+00 | yes |
| arrLo | -0.209166015 | -0.209166015 | 1.3e-16 | yes |
| arrHi | -0.01345729322 | -0.01345729322 | 9.3e-15 | yes |
| rrr | -0.8055555556 | -0.8055555556 | 0.0e+00 | yes |
| rrrLo | -2.064624949 | -2.064624949 | 6.5e-16 | yes |
| rrrHi | -0.06376177122 | -0.06376177122 | 7.0e-15 | yes |
| p1 | 0.25 | 0.25 | 0.0e+00 | yes |
| p2 | 0.1384615385 | 0.1384615385 | 0.0e+00 | yes |
| pChi | 0.0252881358 | 0.0252881358 | 0.0e+00 | yes |
| pFisher | 0.03598088975 | 0.03598088975 | 2.8e-13 | yes |

### RR-2 - Zero cell (0.5 to every cell)

Tool: `relative-risk` - input: `{"a": 0, "b": 40, "c": 6, "d": 39}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| zero | 1 | 1 | 0.0e+00 | yes |
| rr | 0.08630393996 | 0.08630393996 | 0.0e+00 | yes |
| rrLo | 0.00501485195 | 0.00501485195 | 2.6e-15 | yes |
| rrHi | 1.485262203 | 1.485262203 | 2.2e-15 | yes |
| arr | 0.1333333333 | 0.1333333333 | 0.0e+00 | yes |
| arrLo | 0.02070658773 | 0.02070658773 | 3.4e-15 | yes |
| arrHi | 0.2617730183 | 0.2617730183 | 4.2e-16 | yes |
| rrr | 0.91369606 | 0.91369606 | 0.0e+00 | yes |
| rrrLo | -0.4852622027 | -0.4852622027 | 6.9e-15 | yes |
| rrrHi | 0.9949851481 | 0.9949851481 | 0.0e+00 | yes |
| p1 | 0 | 0 | 0.0e+00 | yes |
| p2 | 0.1333333333 | 0.1333333333 | 0.0e+00 | yes |
| pChi | 0.0165979375 | 0.0165979375 | 8.4e-16 | yes |
| pFisher | 0.02739989129 | 0.02739989129 | 9.0e-15 | yes |

### NNT-1 - Trial 15/200 vs 30/200 (ARR CI excludes 0)

Tool: `nnt` - input: `{"a": 15, "b": 185, "c": 30, "d": 170}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| arr | 0.075 | 0.075 | 0.0e+00 | yes |
| arrLo | 0.01282078452 | 0.01282078452 | 1.6e-15 | yes |
| arrHi | 0.1381245384 | 0.1381245384 | 4.0e-16 | yes |
| nnt | 13.33333333 | 13.33333333 | 0.0e+00 | yes |
| nntA | 7.239843201 | 7.239843201 | 3.7e-16 | yes |
| nntB | 77.99834703 | 77.99834703 | 1.6e-15 | yes |
| spans | 0 | 0 | 0.0e+00 | yes |
| pt | 0.075 | 0.075 | 0.0e+00 | yes |
| pc | 0.15 | 0.15 | 0.0e+00 | yes |

### NNT-2 - Trial 12/150 vs 18/150 (ARR CI includes 0)

Tool: `nnt` - input: `{"a": 12, "b": 138, "c": 18, "d": 132}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| arr | 0.04 | 0.04 | 0.0e+00 | yes |
| arrLo | -0.02935004758 | -0.02935004758 | 9.5e-16 | yes |
| arrHi | 0.1102851229 | 0.1102851229 | 6.3e-16 | yes |
| nnt | 25 | 25 | 0.0e+00 | yes |
| nntA | 9.067406138 | 9.067406138 | 7.8e-16 | yes |
| nntB | -34.07149502 | -34.07149502 | 8.3e-16 | yes |
| spans | 1 | 1 | 0.0e+00 | yes |
| pt | 0.08 | 0.08 | 0.0e+00 | yes |
| pc | 0.12 | 0.12 | 0.0e+00 | yes |

### SS-1 - TP90 FP15 FN10 TN85

Tool: `sensitivity-specificity` - input: `{"a": 90, "b": 15, "c": 10, "d": 85}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| sens | 0.9 | 0.9 | 0.0e+00 | yes |
| sensLo | 0.8256343385 | 0.8256343385 | 0.0e+00 | yes |
| sensHi | 0.9447708629 | 0.9447708629 | 0.0e+00 | yes |
| spec | 0.85 | 0.85 | 0.0e+00 | yes |
| specLo | 0.7671644041 | 0.7671644041 | 0.0e+00 | yes |
| specHi | 0.9069401472 | 0.9069401472 | 0.0e+00 | yes |
| ppv | 0.8571428571 | 0.8571428571 | 0.0e+00 | yes |
| ppvLo | 0.7776004677 | 0.7776004677 | 0.0e+00 | yes |
| ppvHi | 0.9114751916 | 0.9114751916 | 0.0e+00 | yes |
| npv | 0.8947368421 | 0.8947368421 | 0.0e+00 | yes |
| npvLo | 0.8169794294 | 0.8169794294 | 1.4e-16 | yes |
| npvHi | 0.9418114757 | 0.9418114757 | 1.2e-16 | yes |
| acc | 0.875 | 0.875 | 0.0e+00 | yes |
| accLo | 0.82198575 | 0.82198575 | 0.0e+00 | yes |
| accHi | 0.9138802552 | 0.9138802552 | 0.0e+00 | yes |
| prev | 0.5 | 0.5 | 0.0e+00 | yes |
| prevLo | 0.4313608596 | 0.4313608596 | 1.3e-16 | yes |
| prevHi | 0.5686391404 | 0.5686391404 | 0.0e+00 | yes |
| lrp | 6 | 6 | 0.0e+00 | yes |
| lrpLo | 3.745828271 | 3.745828271 | 2.4e-16 | yes |
| lrpHi | 9.610691521 | 9.610691521 | 3.7e-16 | yes |
| lrn | 0.1176470588 | 0.1176470588 | 0.0e+00 | yes |
| lrnLo | 0.06497244808 | 0.06497244808 | 4.3e-16 | yes |
| lrnHi | 0.2130261497 | 0.2130261497 | 7.8e-16 | yes |
| zero | 0 | 0 | 0.0e+00 | yes |

### SS-2 - TP27 FP11 FN3 TN159, PPV/NPV at 10% prevalence

Tool: `sensitivity-specificity` - input: `{"a": 27, "b": 11, "c": 3, "d": 159, "pv": 10}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| sens | 0.9 | 0.9 | 0.0e+00 | yes |
| sensLo | 0.7437891742 | 0.7437891742 | 0.0e+00 | yes |
| sensHi | 0.9654001113 | 0.9654001113 | 2.3e-16 | yes |
| spec | 0.9352941176 | 0.9352941176 | 0.0e+00 | yes |
| specLo | 0.8878619661 | 0.8878619661 | 0.0e+00 | yes |
| specHi | 0.9634884598 | 0.9634884598 | 0.0e+00 | yes |
| ppv | 0.7105263158 | 0.7105263158 | 0.0e+00 | yes |
| ppvLo | 0.5524286232 | 0.5524286232 | 2.0e-16 | yes |
| ppvHi | 0.8299672217 | 0.8299672217 | 1.3e-16 | yes |
| npv | 0.9814814815 | 0.9814814815 | 0.0e+00 | yes |
| npvLo | 0.9469750572 | 0.9469750572 | 0.0e+00 | yes |
| npvHi | 0.9936823702 | 0.9936823702 | 0.0e+00 | yes |
| acc | 0.93 | 0.93 | 0.0e+00 | yes |
| accLo | 0.8859451911 | 0.8859451911 | 1.3e-16 | yes |
| accHi | 0.9578478282 | 0.9578478282 | 1.2e-16 | yes |
| prev | 0.15 | 0.15 | 0.0e+00 | yes |
| prevLo | 0.1071359356 | 0.1071359356 | 1.3e-16 | yes |
| prevHi | 0.2060557928 | 0.2060557928 | 4.0e-16 | yes |
| lrp | 13.90909091 | 13.90909091 | 0.0e+00 | yes |
| lrpLo | 7.757928325 | 7.757928325 | 3.4e-16 | yes |
| lrpHi | 24.93743198 | 24.93743198 | 4.3e-16 | yes |
| lrn | 0.106918239 | 0.106918239 | 0.0e+00 | yes |
| lrnLo | 0.03651853936 | 0.03651853936 | 9.5e-16 | yes |
| lrnHi | 0.3130330519 | 0.3130330519 | 8.9e-16 | yes |
| zero | 0 | 0 | 0.0e+00 | yes |
| ppvAdj | 0.6071428571 | 0.6071428571 | 0.0e+00 | yes |
| npvAdj | 0.9882596685 | 0.9882596685 | 0.0e+00 | yes |

### SZ-1 - p 50%, +/-5 points, 95%

Tool: `sample-size-proportion` - input: `{"p": 50, "d": 5, "conf": 95}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| z | 1.959963985 | 1.959963985 | 7.9e-16 | yes |
| n0 | 384.1458821 | 384.1458821 | 1.6e-15 | yes |
| nFpc | 384.1458821 | 384.1458821 | 1.6e-15 | yes |
| n | 385 | 385 | 0.0e+00 | yes |
| ciLo | 45 | 45 | 0.0e+00 | yes |
| ciHi | 55 | 55 | 0.0e+00 | yes |

### SZ-2 - p 15%, +/-3 points, 99%, N 1200

Tool: `sample-size-proportion` - input: `{"p": 15, "d": 3, "conf": 99, "N": 1200}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| z | 2.575829304 | 2.575829304 | 1.0e-15 | yes |
| n0 | 939.9436851 | 939.9436851 | 1.9e-15 | yes |
| nFpc | 527.3315188 | 527.3315188 | 1.1e-15 | yes |
| n | 528 | 528 | 0.0e+00 | yes |
| ciLo | 12 | 12 | 0.0e+00 | yes |
| ciHi | 18 | 18 | 0.0e+00 | yes |

### S2-1 - Two means: difference 5, SD 12, alpha 5%, power 90%, ratio 1:2, 10% loss

Tool: `sample-size-two-groups` - input: `{"m": 1, "delta": 5, "sd": 12, "alpha": 5, "power": 90, "k": 2, "loss": 10}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n1raw | 90.78413525 | 90.78413525 | 1.6e-16 | yes |
| n2raw | 181.5682705 | 181.5682705 | 1.6e-16 | yes |
| n1 | 91 | 91 | 0.0e+00 | yes |
| n2 | 182 | 182 | 0.0e+00 | yes |
| total | 273 | 273 | 0.0e+00 | yes |
| n1L | 102 | 102 | 0.0e+00 | yes |
| n2L | 203 | 203 | 0.0e+00 | yes |
| totalL | 305 | 305 | 0.0e+00 | yes |
| za | 1.959963985 | 1.959963985 | 7.9e-16 | yes |
| zb | 1.281551566 | 1.281551566 | 8.7e-16 | yes |
| diff | 5 | 5 | 0.0e+00 | yes |
| ciLo | 1.980366252 | 1.980366252 | 1.1e-15 | yes |
| ciHi | 8.019633748 | 8.019633748 | 2.2e-16 | yes |

### S2-2 - Two proportions: 40% vs 25%, alpha 5%, power 80%, ratio 1:1

Tool: `sample-size-two-groups` - input: `{"m": 2, "p1": 40, "p2": 25, "alpha": 5, "power": 80, "k": 1, "loss": null}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n1raw | 151.8688621 | 151.8688621 | 9.4e-16 | yes |
| n2raw | 151.8688621 | 151.8688621 | 9.4e-16 | yes |
| n1 | 152 | 152 | 0.0e+00 | yes |
| n2 | 152 | 152 | 0.0e+00 | yes |
| total | 304 | 304 | 0.0e+00 | yes |
| za | 1.959963985 | 1.959963985 | 7.9e-16 | yes |
| zb | 0.8416212336 | 0.8416212336 | 4.0e-16 | yes |
| diff | 15 | 15 | 0.0e+00 | yes |
| ciLo | 4.605721317 | 4.605721317 | 1.9e-15 | yes |
| ciHi | 25.39427868 | 25.39427868 | 4.2e-16 | yes |

### SV-1 - Schoenfeld: HR 0.70, 50% event-free in control, alpha 5%, power 80%, ratio 1:2

Tool: `sample-size-survival` - input: `{"method": 1, "hr": 0.7, "sc": 50, "alpha": 5, "power": 80, "k": 2, "loss": null}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n1raw | 219.0950672 | 219.0950672 | 0.0e+00 | yes |
| n2raw | 438.1901344 | 438.1901344 | 0.0e+00 | yes |
| n1 | 220 | 220 | 0.0e+00 | yes |
| n2 | 440 | 440 | 0.0e+00 | yes |
| total | 660 | 660 | 0.0e+00 | yes |
| d | 277.6354926 | 277.6354926 | 1.0e-15 | yes |
| dN | 278 | 278 | 0.0e+00 | yes |
| st | 61.55722067 | 61.55722067 | 0.0e+00 | yes |
| pe | 0.4229518622 | 0.4229518622 | 0.0e+00 | yes |
| za | 1.959963985 | 1.959963985 | 7.9e-16 | yes |
| zb | 0.8416212336 | 0.8416212336 | 4.0e-16 | yes |
| hrLo | 0.5455079462 | 0.5455079462 | 2.0e-16 | yes |
| hrHi | 0.898245394 | 0.898245394 | 2.5e-16 | yes |

### SV-2 - Freedman: HR 1.50 (harm), 70% event-free in control, alpha 5%, power 90%, ratio 1:1, 10% loss

Tool: `sample-size-survival` - input: `{"method": 2, "hr": 1.5, "sc": 70, "alpha": 5, "power": 90, "k": 1, "loss": 10}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n1raw | 368.1730593 | 368.1730593 | 0.0e+00 | yes |
| n2raw | 368.1730593 | 368.1730593 | 0.0e+00 | yes |
| n1 | 369 | 369 | 0.0e+00 | yes |
| n2 | 369 | 369 | 0.0e+00 | yes |
| total | 738 | 738 | 0.0e+00 | yes |
| n1L | 410 | 410 | 0.0e+00 | yes |
| n2L | 410 | 410 | 0.0e+00 | yes |
| totalL | 820 | 820 | 0.0e+00 | yes |
| d | 262.6855765 | 262.6855765 | 4.3e-16 | yes |
| dN | 263 | 263 | 0.0e+00 | yes |
| st | 58.56620186 | 58.56620186 | 0.0e+00 | yes |
| pe | 0.3571689907 | 0.3571689907 | 0.0e+00 | yes |
| za | 1.959963985 | 1.959963985 | 7.9e-16 | yes |
| zb | 1.281551566 | 1.281551566 | 8.7e-16 | yes |
| hrLo | 1.177922148 | 1.177922148 | 1.9e-16 | yes |
| hrHi | 1.910143216 | 1.910143216 | 2.3e-16 | yes |

### KP-1 - 2x2 table 45/5/10/40, no weights

Tool: `cohens-kappa` - input: `{"w": 0, "t": [[45, 5], [10, 40]]}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| kappaNom | 0.7 | 0.7 | 3.2e-16 | yes |
| seNom | 0.07105631569 | 0.07105631569 | 3.9e-16 | yes |
| kappaLin | 0.7 | 0.7 | 3.2e-16 | yes |
| seLin | 0.07105631569 | 0.07105631569 | 3.9e-16 | yes |
| kappaQuad | 0.7 | 0.7 | 3.2e-16 | yes |
| seQuad | 0.07105631569 | 0.07105631569 | 3.9e-16 | yes |
| n | 100 | 100 | 0.0e+00 | yes |
| po | 0.85 | 0.85 | 0.0e+00 | yes |
| pe | 0.5 | 0.5 | 0.0e+00 | yes |
| kappa | 0.7 | 0.7 | 3.2e-16 | yes |
| se | 0.07105631569 | 0.07105631569 | 3.9e-16 | yes |
| lo | 0.5607321804 | 0.5607321804 | 4.0e-16 | yes |
| hi | 0.8392678196 | 0.8392678196 | 2.6e-16 | yes |

### KP-2 - 3x3 ordinal table, quadratic weights (nominal and linear also compared)

Tool: `cohens-kappa` - input: `{"w": 2, "t": [[30, 8, 2], [6, 25, 9], [1, 7, 12]]}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| kappaNom | 0.4891640867 | 0.4891640867 | 0.0e+00 | yes |
| seNom | 0.07205508186 | 0.07205508186 | 0.0e+00 | yes |
| kappaLin | 0.5566502463 | 0.5566502463 | 0.0e+00 | yes |
| seLin | 0.06692660845 | 0.06692660845 | 0.0e+00 | yes |
| kappaQuad | 0.6328671329 | 0.6328671329 | 3.5e-16 | yes |
| seQuad | 0.06905241466 | 0.06905241466 | 1.0e-15 | yes |
| n | 100 | 100 | 0.0e+00 | yes |
| po | 0.895 | 0.895 | 1.2e-16 | yes |
| pe | 0.714 | 0.714 | 1.6e-16 | yes |
| kappa | 0.6328671329 | 0.6328671329 | 3.5e-16 | yes |
| se | 0.06905241466 | 0.06905241466 | 1.0e-15 | yes |
| lo | 0.4975268871 | 0.4975268871 | 8.9e-16 | yes |
| hi | 0.7682073787 | 0.7682073787 | 0.0e+00 | yes |

### ICC-1 - 12 lesions x 3 raters (diameter in mm), all six forms

Tool: `icc` - input: `{"x": [[21.7, 29.9, 24.7], [25.3, 31.8, 23.7], [46.0, 48.9, 47.5], [22.7, 27.7, 25.5], [33.2, 31.8, 28.0], [28.1, 26.1, 25.9], [34.2, 36.5, 35.8], [36.2, 35.6, 31.7], [27.0, 22.8, 28.3], [23.7, 27.8, 29.3], [23.6, 37.5, 21.0], [39.4, 33.8, 29.7]]}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n | 12 | 12 | 0.0e+00 | yes |
| k | 3 | 3 | 0.0e+00 | yes |
| msb | 127.020202 | 127.020202 | 7.8e-16 | yes |
| msj | 34.38361111 | 34.38361111 | 5.8e-15 | yes |
| mse | 13.1614899 | 13.1614899 | 2.3e-15 | yes |
| msw | 14.93 | 14.93 | 1.3e-15 | yes |
| icc1 | 0.7144955232 | 0.7144955232 | 6.2e-16 | yes |
| icc1Lo | 0.4328158338 | 0.4328158338 | 2.6e-16 | yes |
| icc1Hi | 0.8965192139 | 0.8965192139 | 2.5e-16 | yes |
| icc2 | 0.7176781368 | 0.7176781368 | 4.6e-16 | yes |
| icc2Lo | 0.4363989166 | 0.4363989166 | 1.4e-15 | yes |
| icc2Hi | 0.8977699429 | 0.8977699429 | 3.7e-16 | yes |
| icc3 | 0.7425091274 | 0.7425091274 | 7.5e-16 | yes |
| icc3Lo | 0.4686672783 | 0.4686672783 | 2.1e-15 | yes |
| icc3Hi | 0.9086879472 | 0.9086879472 | 0.0e+00 | yes |
| icc1k | 0.8824596421 | 0.8824596421 | 1.3e-16 | yes |
| icc1kLo | 0.695982773 | 0.695982773 | 0.0e+00 | yes |
| icc1kHi | 0.962950461 | 0.962950461 | 1.2e-16 | yes |
| icc2k | 0.8840736913 | 0.8840736913 | 0.0e+00 | yes |
| icc2kLo | 0.699059304 | 0.699059304 | 7.9e-16 | yes |
| icc2kHi | 0.9634310147 | 0.9634310147 | 3.5e-16 | yes |
| icc3k | 0.8963827038 | 0.8963827038 | 3.7e-16 | yes |
| icc3kLo | 0.7257403374 | 0.7257403374 | 1.1e-15 | yes |
| icc3kHi | 0.9675896805 | 0.9675896805 | 0.0e+00 | yes |

### ICC-2 - Shrout and Fleiss (1979) example, 6 subjects x 4 judges (point estimates equal the paper: 0.17 0.29 0.71 0.44 0.62 0.91)

Tool: `icc` - input: `{"x": [[9, 2, 5, 8], [6, 1, 3, 2], [8, 4, 6, 8], [7, 1, 2, 6], [10, 5, 6, 9], [6, 2, 4, 7]]}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n | 6 | 6 | 0.0e+00 | yes |
| k | 4 | 4 | 0.0e+00 | yes |
| msb | 11.24166667 | 11.24166667 | 0.0e+00 | yes |
| msj | 32.48611111 | 32.48611111 | 0.0e+00 | yes |
| mse | 1.019444444 | 1.019444444 | 4.4e-16 | yes |
| msw | 6.263888889 | 6.263888889 | 1.4e-16 | yes |
| icc1 | 0.1657417684 | 0.1657417684 | 1.7e-16 | yes |
| icc1Lo | -0.1329323249 | -0.1329323249 | 2.1e-15 | yes |
| icc1Hi | 0.7225600623 | 0.7225600623 | 4.6e-16 | yes |
| icc2 | 0.2897637795 | 0.2897637795 | 0.0e+00 | yes |
| icc2Lo | 0.01878651337 | 0.01878651337 | 2.8e-15 | yes |
| icc2Hi | 0.7610843696 | 0.7610843696 | 2.9e-16 | yes |
| icc3 | 0.7148407148 | 0.7148407148 | 0.0e+00 | yes |
| icc3Lo | 0.342464765 | 0.342464765 | 6.5e-16 | yes |
| icc3Hi | 0.94585826 | 0.94585826 | 0.0e+00 | yes |
| icc1k | 0.4427971337 | 0.4427971337 | 1.3e-16 | yes |
| icc1kLo | -0.8844421552 | -0.8844421552 | 3.5e-15 | yes |
| icc1kHi | 0.9124154203 | 0.9124154203 | 1.2e-16 | yes |
| icc2k | 0.6200505476 | 0.6200505476 | 0.0e+00 | yes |
| icc2kLo | 0.0711368153 | 0.0711368153 | 2.5e-15 | yes |
| icc2kHi | 0.9272320402 | 0.9272320402 | 0.0e+00 | yes |
| icc3k | 0.9093155424 | 0.9093155424 | 0.0e+00 | yes |
| icc3kLo | 0.6756747138 | 0.6756747138 | 1.6e-16 | yes |
| icc3kHi | 0.9858916782 | 0.9858916782 | 0.0e+00 | yes |

### BA-1 - 20 pairs, new device vs reference (mmHg), difference A - B

Tool: `bland-altman` - input: `{"a": [166, 148, 88, 148, 130, 121, 128, 136, 114, 97, 104, 129, 159, 136, 117, 110, 180, 152, 112, 138], "b": [168, 134, 86, 141, 134, 123, 123, 123, 125, 99, 108, 130, 161, 145, 106, 122, 193, 150, 118, 132], "mode": 1}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n | 20 | 20 | 0.0e+00 | yes |
| bias | -0.4 | -0.4 | 0.0e+00 | yes |
| sd | 7.949842766 | 7.949842766 | 0.0e+00 | yes |
| biasLo | -4.120640943 | -4.120640943 | 4.3e-16 | yes |
| biasHi | 3.320640943 | 3.320640943 | 5.3e-16 | yes |
| loaLo | -15.98169182 | -15.98169182 | 0.0e+00 | yes |
| loaHi | 15.18169182 | 15.18169182 | 0.0e+00 | yes |
| loaLoLo | -22.44950441 | -22.44950441 | 1.6e-16 | yes |
| loaLoHi | -9.513879237 | -9.513879237 | 3.7e-16 | yes |
| loaHiLo | 8.713879237 | 8.713879237 | 4.1e-16 | yes |
| loaHiHi | 21.64950441 | 21.64950441 | 1.6e-16 | yes |
| outside | 0 | 0 | 0.0e+00 | yes |
| slope | -0.03763857861 | -0.03763857861 | 0.0e+00 | yes |
| slopeP | 0.6361702497 | 0.6361702497 | 7.0e-16 | yes |

### BA-2 - 14 pairs, creatinine (umol/L), difference as percent of the mean, proportional bias

Tool: `bland-altman` - input: `{"a": [369, 234, 361, 364, 134, 149, 145, 70, 199, 287, 80, 108, 148, 71], "b": [337, 237, 344, 337, 135, 137, 137, 81, 210, 263, 64, 95, 147, 92], "mode": 2}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n | 14 | 14 | 0.0e+00 | yes |
| bias | 2.311362963 | 2.311362963 | 0.0e+00 | yes |
| sd | 11.87887235 | 11.87887235 | 0.0e+00 | yes |
| biasLo | -4.547293736 | -4.547293736 | 1.6e-15 | yes |
| biasHi | 9.170019662 | 9.170019662 | 7.7e-16 | yes |
| loaLo | -20.97122685 | -20.97122685 | 0.0e+00 | yes |
| loaHi | 25.59395277 | 25.59395277 | 0.0e+00 | yes |
| loaLoLo | -32.98573343 | -32.98573343 | 4.3e-16 | yes |
| loaLoHi | -8.956720264 | -8.956720264 | 1.4e-15 | yes |
| loaHiLo | 13.57944619 | 13.57944619 | 9.2e-16 | yes |
| loaHiHi | 37.60845935 | 37.60845935 | 1.9e-16 | yes |
| outside | 1 | 1 | 0.0e+00 | yes |
| slope | 0.02863735799 | 0.02863735799 | 1.2e-16 | yes |
| slopeP | 0.3807743879 | 0.3807743879 | 4.7e-15 | yes |

### ROC-1 - Blood marker, 25 with and 35 without the condition (60 rows, 12 tied values), higher = condition

Tool: `roc-auc` - input: `{"x": [9.6, 16.1, 3.7, 8.0, 10.7, 12.7, 7.2, 14.6, 5.8, 14.0, 6.4, 1.0, 7.5, 9.7, 15.2, 11.3, 10.4, 11.0, 5.2, 8.1, 19.2, 10.7, 8.6, 17.3, 12.4, 1.0, 15.8, 22.5, 8.6, 5.8, 16.2, 1.6, 8.6, 1.0, 12.6, 14.3, 10.0, 12.2, 9.7, 19.3, 11.7, 5.3, 15.7, 6.5, 17.2, 22.8, 6.6, 5.4, 8.0, 10.8, 9.9, 27.9, 9.2, 12.7, 12.3, 14.3, 19.8, 13.0, 11.0, 13.0], "y": [0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0], "dir": 1}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n1 | 25 | 25 | 0.0e+00 | yes |
| n0 | 35 | 35 | 0.0e+00 | yes |
| auc | 0.8674285714 | 0.8674285714 | 1.3e-16 | yes |
| se | 0.0443499489 | 0.0443499489 | 1.6e-16 | yes |
| aucLo | 0.7805042689 | 0.7805042689 | 1.4e-16 | yes |
| aucHi | 0.954352874 | 0.954352874 | 1.2e-16 | yes |
| hmSe | 0.05075662659 | 0.05075662659 | 0.0e+00 | yes |
| cutoff | 10.7 | 10.7 | 0.0e+00 | yes |
| youden | 0.5942857143 | 0.5942857143 | 0.0e+00 | yes |
| tp | 22 | 22 | 0.0e+00 | yes |
| fp | 10 | 10 | 0.0e+00 | yes |
| fn | 3 | 3 | 0.0e+00 | yes |
| tn | 25 | 25 | 0.0e+00 | yes |
| sens | 0.88 | 0.88 | 0.0e+00 | yes |
| sensLo | 0.7004420608 | 0.7004420608 | 1.6e-16 | yes |
| sensHi | 0.9583318285 | 0.9583318285 | 1.2e-16 | yes |
| spec | 0.7142857143 | 0.7142857143 | 0.0e+00 | yes |
| specLo | 0.5494506514 | 0.5494506514 | 4.0e-16 | yes |
| specHi | 0.8367346376 | 0.8367346376 | 2.7e-16 | yes |
| lrp | 3.08 | 3.08 | 0.0e+00 | yes |
| lrpLo | 1.788674076 | 1.788674076 | 5.0e-16 | yes |
| lrpHi | 5.303593386 | 5.303593386 | 5.0e-16 | yes |
| lrn | 0.168 | 0.168 | 0.0e+00 | yes |
| lrnLo | 0.05693789229 | 0.05693789229 | 8.5e-16 | yes |
| lrnHi | 0.4956980118 | 0.4956980118 | 9.0e-16 | yes |
| zero | 0 | 0 | 0.0e+00 | yes |
| ncut | 1 | 1 | 0.0e+00 | yes |

### ROC-2 - 5-point rating scale, 18 with and 22 without the condition (many ties), lower = condition

Tool: `roc-auc` - input: `{"x": [4, 2, 1, 1, 2, 1, 2, 1, 3, 4, 1, 5, 1, 4, 3, 1, 4, 4, 4, 5, 4, 3, 2, 5, 3, 5, 2, 4, 3, 1, 1, 3, 2, 4, 3, 1, 3, 3, 2, 4], "y": [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0], "dir": 2}` - result: **MATCH**

| value | Python (reference) | JS (page code) | relative diff | ok |
|---|---|---|---|---|
| n1 | 18 | 18 | 0.0e+00 | yes |
| n0 | 22 | 22 | 0.0e+00 | yes |
| auc | 0.6931818182 | 0.6931818182 | 4.8e-16 | yes |
| se | 0.08417421766 | 0.08417421766 | 0.0e+00 | yes |
| aucLo | 0.5282033831 | 0.5282033831 | 8.4e-16 | yes |
| aucHi | 0.8581602532 | 0.8581602532 | 2.6e-16 | yes |
| hmSe | 0.08542878073 | 0.08542878073 | 0.0e+00 | yes |
| cutoff | 2 | 2 | 0.0e+00 | yes |
| youden | 0.3383838384 | 0.3383838384 | 0.0e+00 | yes |
| tp | 11 | 11 | 0.0e+00 | yes |
| fp | 6 | 6 | 0.0e+00 | yes |
| fn | 7 | 7 | 0.0e+00 | yes |
| tn | 16 | 16 | 0.0e+00 | yes |
| sens | 0.6111111111 | 0.6111111111 | 0.0e+00 | yes |
| sensLo | 0.386190416 | 0.386190416 | 4.3e-16 | yes |
| sensHi | 0.7969475343 | 0.7969475343 | 1.4e-16 | yes |
| spec | 0.7272727273 | 0.7272727273 | 0.0e+00 | yes |
| specLo | 0.5184826599 | 0.5184826599 | 4.3e-16 | yes |
| specHi | 0.8684923971 | 0.8684923971 | 0.0e+00 | yes |
| lrp | 2.240740741 | 2.240740741 | 0.0e+00 | yes |
| lrpLo | 1.031774911 | 1.031774911 | 6.5e-16 | yes |
| lrpHi | 4.866293038 | 4.866293038 | 9.1e-16 | yes |
| lrn | 0.5347222222 | 0.5347222222 | 0.0e+00 | yes |
| lrnLo | 0.2839006343 | 0.2839006343 | 3.9e-16 | yes |
| lrnHi | 1.007140599 | 1.007140599 | 6.6e-16 | yes |
| zero | 0 | 0 | 0.0e+00 | yes |
| ncut | 1 | 1 | 0.0e+00 | yes |

