Independent verification of all financial calculations against authoritative sources
pass rate
Every financial calculation in RetIQ's projection engine is tested against authoritative sources: IRS publications and revenue procedures, Social Security Administration actuarial tables, CMS Medicare data, HHS Federal Poverty Level guidelines, ACA Premium Tax Credit tables, and enacted legislation (SECURE 2.0, OBBB Act, ARPA/IRA enhanced subsidies). Tests use exact bracket boundaries, known-answer scenarios from IRS worksheets, and complete lookup tables verified entry-by-entry. Tolerance is $1 for rounding unless otherwise noted.
| Test | Expected | Actual | Result |
|---|---|---|---|
| Top of 10% bracket ($24,800) | 2,480 | 2,480 | ✓ |
| Top of 12% bracket ($100,800) | 11,600 | 11,600 | ✓ |
| Top of 22% bracket ($211,400) | 35,932 | 35,932 | ✓ |
| Top of 24% bracket ($403,550) | 82,048 | 82,048 | ✓ |
| Top of 32% bracket ($512,450) | 116,896 | 116,896 | ✓ |
| Top of 35% bracket ($768,700) | 206,584 | 206,584 | ✓ |
| In 37% bracket ($1,000,000) | 292,165 | 292,165 | ✓ |
| Zero income | 0 | 0 | ✓ |
| Mid-bracket $50,000 | 5,504 | 5,504 | ✓ |
| Mid-bracket $150,000 | 22,424 | 22,424 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Top of 10% bracket ($12,400) | 1,240 | 1,240 | ✓ |
| Top of 12% bracket ($50,400) | 5,800 | 5,800 | ✓ |
| Top of 22% bracket ($105,700) | 17,966 | 17,966 | ✓ |
| Top of 24% bracket ($201,775) | 41,024 | 41,024 | ✓ |
| Mid-bracket $75,000 | 11,212 | 11,212 | ✓ |
| $200,000 income | 40,598 | 40,598 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| MFJ base deduction | 32,200 | 32,200 | ✓ |
| Single base deduction | 16,100 | 16,100 | ✓ |
| Senior additional — MFJ (per person) | 1,650 | 1,650 | ✓ |
| Senior additional — Single | 2,050 | 2,050 | ✓ |
| OBBB Senior Deduction: $6,000 per person | 6,000 | 6,000 | ✓ |
| OBBB phaseout threshold — MFJ | 150,000 | 150,000 | ✓ |
| OBBB phaseout threshold — Single | 75,000 | 75,000 | ✓ |
| OBBB phaseout rate: 6% ($60 per $1,000 over threshold) | 0.06 | 0.06 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| In-window: MFJ both 65+ with $80K pension gets $12K OBBB | 3,404 | 3,404 | ✓ |
| Post-sunset 2029: MFJ both 65+ loses $12K OBBB deduction | 4,844 | 4,844 | ✓ |
| Sunset delta = $12K × 12% bracket = $1,440 | 1,440 | 1,440 | ✓ |
| In-window: Single 65 with $100K MAGI gets partial OBBB ($4.5K) | 11,729 | 11,729 | ✓ |
| Post-sunset: Single 65 with $100K MAGI loses the partial $4.5K | 990 | 990 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Bug-reveal: MFJ primary 60 / spouse 67 in-window gets spouse-only seniors | 4,322 | 4,322 | ✓ |
| Bug-reveal: MFJ primary 60 / spouse 67 post-sunset 2030 gets §63(f) only | 5,042 | 5,042 | ✓ |
| Regression: MFJ both 65+ in-window unchanged | 3,404 | 3,404 | ✓ |
| Regression: MFJ both <65 unchanged (no senior, no OBBB) | 5,240 | 5,240 | ✓ |
| Regression: Single 65+ in-window with phase-out unchanged | 11,729 | 11,729 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| In-window MFJ with capGains: Smart Fill produces consistent year output | 1 | 1 | ✓ |
| Below-threshold MFJ regression: capGains=0 leaves Smart Fill unchanged | 1 | 1 | ✓ |
| Above-cliff MFJ: capGains pushes AGI past $250K → OBBB fully phased out | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Year-rows for 2028 and 2029 present in projection | 1 | 1 | ✓ |
| fedTax > 0 in 2028 (plan exercises tax path) | 1 | 1 | ✓ |
| fedTax 2028→2029 delta = $6,000 × 12% bracket = $720 (OBBB sunset boundary) | 720 | 720 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| qbiDeduction is state-independent (sanity, equals $15,367 hand-derived) | 15,367 | 15,367 | ✓ |
| PA non-conformer + $100K SE: stateTax = $2,853 (gross-base × 3.07%) | 2,853 | 2,853 | ✓ |
| CA non-conformer + $100K SE: stateTax = $5,296 (gross-base through CA brackets) | 5,296 | 5,296 | ✓ |
| NJ non-conformer + $100K SE: stateTax = $3,794 (gross-base through NJ brackets) | 3,794 | 3,794 | ✓ |
| NY AGI-state gross-base + $100K SE: stateTax = $4,915 (§199A absent below AGI line) | 4,915 | 4,915 | ✓ |
| ID conformer + $100K SE: stateTax = $4,111 (§199A flows through, flat 5.3%) | 4,111 | 4,111 | ✓ |
| IA conformer + $100K SE: stateTax = $2,948 (§199A flows through, flat 3.8%) | 2,948 | 2,948 | ✓ |
| ND conformer + $100K SE: stateTax = $567 (§199A flows through, ND brackets) | 567 | 567 | ✓ |
| CO conformer + $100K SE (below $500K threshold): stateTax = $3,413 (§199A flows through, flat 4.4%) | 3,413 | 3,413 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| CO: above $1M MFJ AGI — §199A subtraction suppressed (F-197) | 52,800 | 52,800 | ✓ |
| Florida — no income tax | 0 | 0 | ✓ |
| Texas — no income tax | 0 | 0 | ✓ |
| California — progressive brackets on $100K | 3,246 | 3,246 | ✓ |
| New York — progressive brackets on $100K (2026 rates) | 5,068 | 5,068 | ✓ |
| Maryland — progressive brackets only on $100K (local tax tested separately) | 4,698 | 4,698 | ✓ |
| Illinois — flat 4.95% on $100K | 4,950 | 4,950 | ✓ |
| Pennsylvania — flat 3.07% on $100K | 3,070 | 3,070 | ✓ |
| Nevada — no income tax | 0 | 0 | ✓ |
| Washington — no income tax | 0 | 0 | ✓ |
| No state selected — zero tax | 0 | 0 | ✓ |
| MD: $100K MFJ with 3.2% local rate | 7,898 | 7,898 | ✓ |
| MD: local rate 0 = state-only (backward compat) | 4,698 | 4,698 | ✓ |
| MD: $100K MFJ age 65 with local + retirement exclusion | 1 | 1 | ✓ |
| FL: mdLocalTaxRate ignored for non-MD state | 0 | 0 | ✓ |
| FL: SS+IRA income, no tax | 0 | 0 | ✓ |
| TX: $200K income, no tax | 0 | 0 | ✓ |
| WA: any income, no tax | 0 | 0 | ✓ |
| NH: IRA distribution, no tax | 0 | 0 | ✓ |
| none: federal only, no state tax | 0 | 0 | ✓ |
| IL: SS+IRA both fully exempt | 0 | 0 | ✓ |
| IL: earned income taxable (4.95%) | 4,950 | 4,950 | ✓ |
| IL: mixed — wages taxable, IRA exempt | 2,475 | 2,475 | ✓ |
| IL: $100K Roth conversion fully exempt | 0 | 0 | ✓ |
| IL: $50K TAD fully exempt | 0 | 0 | ✓ |
| IL: mixed wages + conversion (only conversion exempt) | 4,950 | 4,950 | ✓ |
| PA: $40K Roth conversion exempt at 60+ | 0 | 0 | ✓ |
| PA: $40K conversion taxable under 60 | 1,228 | 1,228 | ✓ |
| NY: conversion competes with pension for $20K cap | 3,988 | 3,988 | ✓ |
| PA: IRA exempt age 60+ | 0 | 0 | ✓ |
| PA: IRA taxable age 58 (3.07%) | 1,535 | 1,535 | ✓ |
| PA: SS always exempt (all ages) | 0 | 0 | ✓ |
| CA: MFJ $80K — progressive brackets | 2,046 | 2,046 | ✓ |
| CA: SS exempt, IRA taxable | 804 | 804 | ✓ |
| CA: single filer pays more than MFJ same income | 1 | 1 | ✓ |
| NY: MFJ $100K with $20K pension exclusion | 3,988 | 3,988 | ✓ |
| NY: SS exempt from state tax | 1 | 1 | ✓ |
| KS: SS exempt under $75K AGI | 1 | 1 | ✓ |
| KS: SS taxable above $75K AGI | 1 | 1 | ✓ |
| CT: SS exempt under $150K MFJ AGI | 1 | 1 | ✓ |
| RI: SS exempt under $114,900 AGI | 1 | 1 | ✓ |
| Invalid state code returns $0 | 0 | 0 | ✓ |
| Zero income returns $0 | 0 | 0 | ✓ |
| PA age gate: $40K IRA age 58 taxable | 1,228 | 1,228 | ✓ |
| PA age gate: $40K IRA age 60 exempt | 0 | 0 | ✓ |
| NC: flat 3.99% on $100K | 3,990 | 3,990 | ✓ |
| IN: flat 2.95% on $100K | 2,950 | 2,950 | ✓ |
| KY: flat 3.5% on $100K | 3,500 | 3,500 | ✓ |
| MS: flat 4.0% on $100K | 4,000 | 4,000 | ✓ |
| ID: flat 5.3% on $100K | 5,300 | 5,300 | ✓ |
| GA: flat 5.19% on $100K | 5,190 | 5,190 | ✓ |
| CO: flat 4.4% on $100K | 4,400 | 4,400 | ✓ |
| UT: flat 4.5% on $100K | 4,500 | 4,500 | ✓ |
| OH: $100K MFJ — 2.75% above $26,050 | 2,034 | 2,034 | ✓ |
| OH: $20K income — below $26,050 threshold, $0 tax | 0 | 0 | ✓ |
| MT: $60K MFJ — all at 4.7% (below $95K threshold) | 2,820 | 2,820 | ✓ |
| MT: $120K MFJ — split bracket | 5,878 | 5,878 | ✓ |
| MT: single $60K pays more than MFJ $60K (lower threshold) | 1 | 1 | ✓ |
| NE: $100K MFJ — three brackets (top rate 4.55%) | 4,284 | 4,284 | ✓ |
| OK: $20K MFJ — four-bracket structure (HB2764) | 471 | 471 | ✓ |
| SC: $100K MFJ — 2026 brackets (top rate 6.0%) | 5,344 | 5,344 | ✓ |
| F-052: MD MFJ $100K, mdLocalTaxRate=3.30 adds $3,300 | 7,998 | 7,998 | ✓ |
| F-052: MD MFJ $100K, mdLocalTaxRate=3.00 (Montgomery) | 7,698 | 7,698 | ✓ |
| F-052: MD MFJ $100K, mdLocalTaxRate=0 yields bracket-only result | 4,698 | 4,698 | ✓ |
| F-052: VA $100K MFJ passthrough (no localRate field) | 5,493 | 5,493 | ✓ |
| F-052: MD MFJ $30K low income, mdLocalTaxRate=3.30 | 2,363 | 2,363 | ✓ |
| F-052: MD MFJ $250K high income, mdLocalTaxRate=3.30 | 20,573 | 20,573 | ✓ |
| F-055: NJ MFJ $80K gross, $40K pension, age 65 — Tier 1 (full) | 630 | 630 | ✓ |
| F-055: NJ MFJ $110K gross, $40K pension, age 65 — Tier 2 (50%) | 1,295 | 1,295 | ✓ |
| F-055: NJ MFJ $130K gross, $40K pension, age 65 — Tier 3 (25%) | 3,026 | 3,026 | ✓ |
| F-055: NJ MFJ $160K gross, $40K pension, age 65 — Cliff (0%) | 6,150 | 6,150 | ✓ |
| F-055: NJ Single $80K gross, $40K pension, age 65 — Tier 1 Single ($75K max) | 718 | 718 | ✓ |
| F-055: NJ Single $130K gross, $40K pension, age 65 — Tier 3 Single (25% × $75K) | 4,960 | 4,960 | ✓ |
| F-055: NJ MFJ age 60 (under 62) — exclusion ineligible | 1,645 | 1,645 | ✓ |
| F-055: NJ MFJ age 62 just-eligible — full exclusion at $80K | 630 | 630 | ✓ |
| F-055: NJ MFJ exactly $100K gross — Tier 1 boundary (100%) | 1,050 | 1,050 | ✓ |
| F-055: NJ MFJ exactly $150K gross — Tier 3 boundary (25%, not cliff) | 4,131 | 4,131 | ✓ |
| Local rate adds flat charge on state base (NC 3.99% + 1.5% local) | 5,490 | 5,490 | ✓ |
| localTaxRate default 0 is a no-op (NC flat 3.99% baseline) | 3,990 | 3,990 | ✓ |
| MD ignores generic localTaxRate (no double-count with mdLocalTaxRate) | 1 | 1 | ✓ |
| Local rate respects exemption base (PA all-retirement at 60+ is $0) | 0 | 0 | ✓ |
| Override replaces the bracket model (NC 5% on gross) | 5,000 | 5,000 | ✓ |
| Override bypasses exemptions and the zero-base early return (PA retiree) | 2,000 | 2,000 | ✓ |
| Override (state) and local rate stack (NC 5% + 1% local) | 6,000 | 6,000 | ✓ |
| Override null is a no-op (NC bracket model unchanged) | 3,990 | 3,990 | ✓ |
| OH earned-base: retiree with no wages, local rate is a no-op | 1,484 | 1,484 | ✓ |
| OH earned-base: local rate applies to wages (worker) | 1,659 | 1,659 | ✓ |
| Untagged state stays broad-base (NC retiree, contrast with OH) | 3,992 | 3,992 | ✓ |
| PA earned-base: local rate applies to wages (worker under 60) | 2,442 | 2,442 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Below base threshold: $20K SS, $10K other → 0% taxable | 0 | 0 | ✓ |
| At base threshold: $24K SS, $20K other → 0% taxable | 0 | 0 | ✓ |
| Between thresholds: $30K SS, $25K other → partial | 4,000 | 4,000 | ✓ |
| Above upper: $30K SS, $60K other → 85% rule | 25,500 | 25,500 | ✓ |
| Very high income: $40K SS, $200K other → 85% cap | 34,000 | 34,000 | ✓ |
| Zero SS benefit → zero taxable | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Below base: $20K SS, $10K other → 0% | 0 | 0 | ✓ |
| Between thresholds: $24K SS, $20K other → partial | 3,500 | 3,500 | ✓ |
| Above upper: $30K SS, $40K other → 85% rule | 22,350 | 22,350 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Low earner AIME=$1,000 → PIA=$900 | 900 | 900 | ✓ |
| Mid earner AIME=$5,000 → PIA≈$2,346 | 2,345.8 | 2,345 | ✓ |
| Max earner AIME=$15,375 → PIA≈$4,369 | 4,369.4 | 4,369 | ✓ |
| Income above wage cap ($200K) capped at $184,500 | 4,369 | 4,369 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Born 1937 → FRA 65y 0m | 780 | 780 | ✓ |
| Born 1938 → FRA 65y 2m | 782 | 782 | ✓ |
| Born 1939 → FRA 65y 4m | 784 | 784 | ✓ |
| Born 1940 → FRA 65y 6m | 786 | 786 | ✓ |
| Born 1941 → FRA 65y 8m | 788 | 788 | ✓ |
| Born 1942 → FRA 65y 10m | 790 | 790 | ✓ |
| Born 1943 → FRA 66y 0m | 792 | 792 | ✓ |
| Born 1954 → FRA 66y 0m | 792 | 792 | ✓ |
| Born 1955 → FRA 66y 2m | 794 | 794 | ✓ |
| Born 1956 → FRA 66y 4m | 796 | 796 | ✓ |
| Born 1957 → FRA 66y 6m | 798 | 798 | ✓ |
| Born 1958 → FRA 66y 8m | 800 | 800 | ✓ |
| Born 1959 → FRA 66y 10m | 802 | 802 | ✓ |
| Born 1960+ → FRA 67y 0m | 804 | 804 | ✓ |
| Born 1975 → FRA 67y 0m | 804 | 804 | ✓ |
| Born 1990 → FRA 67y 0m | 804 | 804 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Claim at FRA (67) → 100% of PIA | 2,000 | 2,000 | ✓ |
| Claim at 62 (60 months early) → 70% of PIA | 1,400 | 1,400 | ✓ |
| Claim at 64 (36 months early) → 80% of PIA | 1,600 | 1,600 | ✓ |
| Claim at 65 (24 months early) → ~86.7% of PIA | 1,733 | 1,733 | ✓ |
| Claim at 70 (36 months late) → 124% of PIA | 2,480 | 2,480 | ✓ |
| Claim at 68 (12 months late) → 108% of PIA | 2,160 | 2,160 | ✓ |
| Claim at 72 → same as 70 (DRC capped) | 2,480 | 2,480 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| PIA $3000 → claim 70 → reverse recovers ~$3000 | 3,000 | 3,000 | ✓ |
| PIA $2500 → claim 62 → reverse recovers ~$2500 | 2,500 | 2,500 | ✓ |
| PIA $2000 → claim 67 (FRA) → reverse recovers $2000 exactly | 2,000 | 2,000 | ✓ |
| PIA $4000 → claim 65 → reverse recovers ~$4000 | 4,000 | 3,999 | ✓ |
| Zero current monthly returns 0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Manual PIA claim at FRA: 5yr pre-claim COLA accumulates | 39,747 | 39,747 | ✓ |
| Manual PIA claim at 70: 8yr COLA + 24% DRC | 52,303 | 52,303 | ✓ |
| Already collecting: year 0 = entered (no COLA back-application) | 30,000 | 30,000 | ✓ |
| Already collecting: year 5 = entered × 1.02^5 | 33,122 | 33,122 | ✓ |
| estimateSS path: 5yr COLA ratio matches 1.02^5 | 11,041 | 11,041 | ✓ |
| Couple manual PIAs: combined y.ss reflects both pre-claim COLAs | 59,620 | 59,620 | ✓ |
| Spouse estimateSS path: 5yr COLA ratio matches 1.02^5 | 11,041 | 11,041 | ✓ |
| Cohort-stable manual PIA (birthYear 1980): same COLA accumulation as 1964 cohort | 39,747 | 39,747 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Age 72 divisor = 27.4 | 27.4 | 27.4 | ✓ |
| Age 73 divisor = 26.5 | 26.5 | 26.5 | ✓ |
| Age 74 divisor = 25.5 | 25.5 | 25.5 | ✓ |
| Age 75 divisor = 24.6 | 24.6 | 24.6 | ✓ |
| Age 76 divisor = 23.7 | 23.7 | 23.7 | ✓ |
| Age 77 divisor = 22.9 | 22.9 | 22.9 | ✓ |
| Age 78 divisor = 22 | 22 | 22 | ✓ |
| Age 79 divisor = 21.1 | 21.1 | 21.1 | ✓ |
| Age 80 divisor = 20.2 | 20.2 | 20.2 | ✓ |
| Age 81 divisor = 19.4 | 19.4 | 19.4 | ✓ |
| Age 82 divisor = 18.5 | 18.5 | 18.5 | ✓ |
| Age 83 divisor = 17.7 | 17.7 | 17.7 | ✓ |
| Age 84 divisor = 16.8 | 16.8 | 16.8 | ✓ |
| Age 85 divisor = 16 | 16 | 16 | ✓ |
| Age 86 divisor = 15.2 | 15.2 | 15.2 | ✓ |
| Age 87 divisor = 14.4 | 14.4 | 14.4 | ✓ |
| Age 88 divisor = 13.7 | 13.7 | 13.7 | ✓ |
| Age 89 divisor = 12.9 | 12.9 | 12.9 | ✓ |
| Age 90 divisor = 12.2 | 12.2 | 12.2 | ✓ |
| Age 91 divisor = 11.5 | 11.5 | 11.5 | ✓ |
| Age 92 divisor = 10.8 | 10.8 | 10.8 | ✓ |
| Age 93 divisor = 10.1 | 10.1 | 10.1 | ✓ |
| Age 94 divisor = 9.5 | 9.5 | 9.5 | ✓ |
| Age 95 divisor = 8.9 | 8.9 | 8.9 | ✓ |
| Age 96 divisor = 8.4 | 8.4 | 8.4 | ✓ |
| Age 97 divisor = 7.8 | 7.8 | 7.8 | ✓ |
| Age 98 divisor = 7.3 | 7.3 | 7.3 | ✓ |
| Age 99 divisor = 6.8 | 6.8 | 6.8 | ✓ |
| Age 100 divisor = 6.4 | 6.4 | 6.4 | ✓ |
| Age 101 divisor = 6 | 6 | 6 | ✓ |
| Age 102 divisor = 5.6 | 5.6 | 5.6 | ✓ |
| Age 103 divisor = 5.2 | 5.2 | 5.2 | ✓ |
| Age 104 divisor = 4.9 | 4.9 | 4.9 | ✓ |
| Age 105 divisor = 4.6 | 4.6 | 4.6 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Age 73, $1M balance → $37,736 | 37,736 | 37,736 | ✓ |
| Age 80, $1M balance → $49,505 | 49,505 | 49,505 | ✓ |
| Age 90, $1M balance → $81,967 | 81,967 | 81,967 | ✓ |
| Age 100, $500K balance → $78,125 | 78,125 | 78,125 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Table II: owner 75 / spouse 64 = 25.3 (IRS worked example) | 25.3 | 25.3 | ✓ |
| Table II: owner 73 / spouse 62 = 27.2 (11-yr gap boundary) | 27.2 | 27.2 | ✓ |
| Table II: owner 75 / spouse 51 = 35.8 (24-yr gap) | 35.8 | 35.8 | ✓ |
| Table II: owner 100 / spouse 89 = 6.8 (oldest at 11-yr gap) | 6.8 | 6.8 | ✓ |
| Table II: owner 120 / spouse 109 = 2.0 (table minimum) | 2 | 2 | ✓ |
| calcRMD uses Table II for 75/51 + sole beneficiary flag | 27,933 | 27,933 | ✓ |
| Exactly 10-year gap falls back to Uniform Lifetime | 40,650 | 40,650 | ✓ |
| Flag off → Uniform Lifetime even at 24-yr gap | 40,650 | 40,650 | ✓ |
| Missing Table II cell falls back to Uniform Lifetime | 40,650 | 40,650 | ✓ |
| usesJointLifeTable(75, 51, true) → true (24-yr gap + flag) | 1 | 1 | ✓ |
| usesJointLifeTable(75, 65, true) → false (exactly 10-yr gap) | 0 | 0 | ✓ |
| usesJointLifeTable(75, 51, false) → false (flag off) | 0 | 0 | ✓ |
| usesJointLifeTable(75, 80, true) → false (spouse older) | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Born 1950 → RMD starts at 72 | 72 | 72 | ✓ |
| Born 1951 → RMD starts at 73 | 73 | 73 | ✓ |
| Born 1959 → RMD starts at 73 | 73 | 73 | ✓ |
| Born 1960 → RMD starts at 75 | 75 | 75 | ✓ |
| Born 1980 → RMD starts at 75 | 75 | 75 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Tax rate = 3.8% | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| AGI $100K + tax-exempt $5K = $105K | 105,000 | 105,000 | ✓ |
| AGI $200K + zero tax-exempt = $200K (regression baseline) | 200,000 | 200,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| AGI $40K + ss $30K + ssTaxable $22K → $48K | 48,000 | 48,000 | ✓ |
| AGI $80K + zero SS → $80K (regression baseline) | 80,000 | 80,000 | ✓ |
| F-161 hand-computed: AGI $55,350 + ss $30K + ssTaxable $15,350 → ACA MAGI $70K | 70,000 | 70,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| AGI $300K + foreign earned $0 → $300K | 300,000 | 300,000 | ✓ |
| AGI $250K + foreign earned $50K → $300K | 300,000 | 300,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Couple $200K W2 + $30K 401(k) deferral → year-0 AGI ≈ $170K | 170,000 | 170,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Year output stores ssTaxable < ss for SS-collecting couple | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2026 (year-0) MFJ 10% top unchanged: $24,800 | 24,800 | 24,800 | ✓ |
| 2030 MFJ 10% top: $24,800 × 1.03^4 floored = $27,900 | 27,900 | 27,900 | ✓ |
| 2036 MFJ 22% top (10yr): $211,400 × 1.03^10 floored | 284,100 | 284,100 | ✓ |
| Top bracket (Infinity) preserved unchanged | 1 | 1 | ✓ |
| Shared-boundary contract: bracket[N+1].min === bracket[N].max post-index | 1 | 1 | ✓ |
| inflation=0 invariance: brackets unchanged at year >0 | 201,775 | 201,775 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2026 MFJ year-0: $32,200 | 32,200 | 32,200 | ✓ |
| 2036 MFJ: $32,200 × 1.03^10 floored | 43,250 | 43,250 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2030 MFJ per-spouse: $1,650 × 1.03^4 floored | 1,850 | 1,850 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2025 base year: $6,000 (year-zero, in-window) | 6,000 | 6,000 | ✓ |
| 2027 (in-window): $6,000 fixed (no indexing) | 6,000 | 6,000 | ✓ |
| 2029 (post-sunset): returns 0 | 0 | 0 | ✓ |
| 2024 (pre-window): returns 0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2027 MFJ: $150,000 fixed (no indexing) | 150,000 | 150,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2030 MFJ 0% threshold: $98,900 × 1.03^4 floored | 111,300 | 111,300 | ✓ |
| 2036 Single 0% threshold: $49,450 × 1.03^10 floored | 66,450 | 66,450 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2030 MFJ start: $403,500 × 1.03^4 floored | 454,100 | 454,100 | ✓ |
| 2036 MFJ end: $553,500 × 1.03^10 floored | 743,850 | 743,850 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| taxBracketsFor at base year returns same .max as REGS | 211,400 | 211,400 | ✓ |
| stdDeductionFor at base year preserves raw value | 32,200 | 32,200 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2036 MFJ 22% bracket top > 2026 22% bracket top | 1 | 1 | ✓ |
| 2036 MFJ std deduction > 2026 std deduction | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2026 (year-0) tier 1 MFJ unchanged: $218,000 | 218,000 | 218,000 | ✓ |
| 2026 (year-0) tier 5 (top) MFJ unchanged: $750,000 | 750,000 | 750,000 | ✓ |
| inflation=0 returns identical tiers across years | 274,000 | 274,000 | ✓ |
| 2030 tier 1 MFJ: $218,000 × 1.03^4 round-to-1000 | 245,000 | 245,000 | ✓ |
| 2036 tier 1 MFJ: $218,000 × 1.03^10 round-to-1000 | 293,000 | 293,000 | ✓ |
| 2036 tier 4 MFJ: $410,000 × 1.03^10 round-to-1000 | 551,000 | 551,000 | ✓ |
| 2030 tier 1 Single: $109,000 × 1.03^4 round-to-1000 | 123,000 | 123,000 | ✓ |
| 2027 top tier MFJ stays at $750,000 (frozen pre-2028) | 750,000 | 750,000 | ✓ |
| 2028 top tier MFJ stays at $750,000 (year-zero for top tier) | 750,000 | 750,000 | ✓ |
| 2030 top tier MFJ: $750,000 × 1.03^2 round-to-1000 | 796,000 | 796,000 | ✓ |
| 2036 top tier MFJ: $750,000 × 1.03^8 round-to-1000 | 950,000 | 950,000 | ✓ |
| Tier 0 (magi: 0) baseline preserved across years | 0 | 0 | ✓ |
| Tier 1 partB surcharge preserved across years | 3,409.2 | 3,409.2 | ✓ |
| $1,000 round-to-nearest: tier 2 MFJ at 2028 | 291,000 | 291,000 | ✓ |
| calcIRMAA at 2030 with $230K MFJ uses indexed tier 1 ($245K) | 2,435 | 2,435 | ✓ |
| calcIRMAA at 2030 inflation=0 uses 2026 thresholds | 3,409 | 3,409 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Tier 0: below $218K — Part B annual | 2,435 | 2,435 | ✓ |
| Tier 0: below $218K — Part D annual | 0 | 0 | ✓ |
| Tier 1: $218K–$274K — Part B annual | 3,409 | 3,409 | ✓ |
| Tier 1: $218K–$274K — Part D annual | 174 | 174 | ✓ |
| Tier 2: $274K–$342K — Part B annual | 4,870 | 4,870 | ✓ |
| Tier 2: $274K–$342K — Part D annual | 450 | 450 | ✓ |
| Tier 3: $342K–$410K — Part B annual | 6,330 | 6,330 | ✓ |
| Tier 3: $342K–$410K — Part D annual | 725 | 725 | ✓ |
| Tier 4: $410K–$750K — Part B annual | 7,790 | 7,790 | ✓ |
| Tier 4: $410K–$750K — Part D annual | 1,000 | 1,000 | ✓ |
| Tier 5: above $750K — Part B annual | 8,279 | 8,279 | ✓ |
| Tier 5: above $750K — Part D annual | 1,092 | 1,092 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Tier 0: below $109K — Part B annual | 2,435 | 2,435 | ✓ |
| Tier 1: $109K–$137K — Part B annual | 3,409 | 3,409 | ✓ |
| Tier 5: above $500K — Part B annual | 8,279 | 8,279 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Base Part B premium = $202.90/month ($2,434.80/year) | 2,435 | 2,435 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Couple plan: spAge at year 0 = configured spouseAge | 62 | 62 | ✓ |
| Couple plan: spAge increments by year offset | 67 | 67 | ✓ |
| Couple plan: primaryDead/spouseDead false while both alive | 1 | 1 | ✓ |
| Single plan: spAge is null | 1 | 1 | ✓ |
| Spouse-dies plan: spAge numeric while spouse alive (primary 72) | 69 | 69 | ✓ |
| Spouse-dies plan: spAge null after spouse death (primary 75) | 1 | 1 | ✓ |
| Spouse-dies plan: primaryDead stays false post spouse death | 1 | 1 | ✓ |
| Primary-dies plan: primaryDead true post primary death | 1 | 1 | ✓ |
| Primary-dies plan: spAge still numeric (survivor age) post primary death | 72 | 72 | ✓ |
| Primary-dies plan: spouseDead stays false while spouse alive | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Couple both 65+ in MFJ Tier 1 → IRMAA = 2× per-person surcharge | 2,296 | 2,296 | ✓ |
| Couple, only primary 65+ → IRMAA = 1× per-person surcharge | 1,148 | 1,148 | ✓ |
| Single filer 65+ in single Tier 1 → IRMAA = 1× per-person surcharge | 1,148 | 1,148 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Post-fix: y.medicareCost + y.irmaa equals authority total at yr=3 | 8,296 | 8,296 | ✓ |
| y.irmaa grows at exactly medicareInflation% nominal year-over-year (same tier) | 0 | 0 | ✓ |
| y.medicareCost grows at exactly medicareInflation% nominal year-over-year | 0 | 1 | ✓ |
| Tier 0: y.medicareCost = base × enrollees × (1+m)^yr, y.irmaa = 0 | 5,638 | 5,638 | ✓ |
| Tier 0: y.irmaa = 0 (no surcharge below MFJ Tier 1 threshold) | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Couple both 65+: y.expenses includes Medicare cost (post-65 > pre-65 expenses) | 1 | 1 | ✓ |
| Single 65+: y.expenses includes 1× Medicare cost | 1 | 1 | ✓ |
| Pre-65: medicareCost is 0 (no Medicare in expenses) | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Default Medicare inflation 5%/yr (no param) | 3,966 | 3,966 | ✓ |
| Custom Medicare inflation 3%/yr honored | 3,272 | 3,272 | ✓ |
| 0% Medicare inflation honored (?? not ||) | 2,435 | 2,435 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Single, priorYearMinus2MAGI=$200K → year 0 surcharge matches Tier 3 per-enrollee from REGS | 4,620 | 4,620 | ✓ |
| Single, priorYearMinus2MAGI=$200K → year 0 surcharge > 0 (proxy was 0) | 1 | 1 | ✓ |
| Single, priorYearMinus2MAGI=0 → year 0 falls to proxy (no surcharge) | 1 | 1 | ✓ |
| MFJ, priorYearMinus1MAGI alone → year 0 unaffected (no priorYearMinus2 override) | 1 | 1 | ✓ |
| MFJ both 65+: year 1 surcharge = 2 × per-enrollee Tier surcharge | 2,296 | 2,296 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Single retired, $150K active stream → year 0 surcharge > 0 (was 0 pre-fix) | 1 | 1 | ✓ |
| Single retired, $150K active stream → year 0 surcharge matches REGS Tier lookup | 2,885 | 2,885 | ✓ |
| Single retired, no incomeStreams → year 0 IRMAA = 0 (Tier 0) | 1 | 1 | ✓ |
| Single retired, $150K stream startAge=70 (inactive at currentAge=65) → year 0 surcharge = 0 | 1 | 1 | ✓ |
| F-165 override precedence — priorYearMinus2MAGI=$50K beats $150K stream proxy | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 401(k) elective deferral: $24,500 | 24,500 | 24,500 | ✓ |
| IRA contribution limit: $7,500 | 7,500 | 7,500 | ✓ |
| 401(k) catch-up (50–59, 64+): $8,000 | 8,000 | 8,000 | ✓ |
| SECURE 2.0 enhanced catch-up (60–63): $11,250 | 11,250 | 11,250 | ✓ |
| IRA catch-up (50+): $1,100 | 1,100 | 1,100 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Annual limit: $111,000 (2026) | 111,000 | 111,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Projection covers 46 years (age 50–95) | 46 | 46 | ✓ |
| Net worth stays positive (no bust) | 1 | 1 | ✓ |
| Pre-retirement savings accumulate | 1 | 1 | ✓ |
| RMDs start at age 75 (born 1976) | 75 | 75 | ✓ |
| Tax computed for retirement year | 1 | 1 | ✓ |
| SS benefits start at claim age 67 | 67 | 67 | ✓ |
| Account balances sum to net worth | 4,736,280 | 4,736,280 | ✓ |
| Higher income → higher lifetime tax | 1 | 1 | ✓ |
| Roth conversion increases tax in conversion year | 1 | 1 | ✓ |
| Roth-first order depletes Roth faster | 1 | 1 | ✓ |
| California adds state tax vs no-state | 1 | 1 | ✓ |
| primaryEarned field present in year output | 1 | 1 | ✓ |
| spouseEarned field present in year output | 1 | 1 | ✓ |
| primaryEarned + spouseEarned === earned (invariant) | 180,000 | 180,000 | ✓ |
| FICA field present in pre-retirement year | 1 | 1 | ✓ |
| Pre-retirement FICA ≈ 7.65% of gross wages (below SS cap) | 9,180 | 9,180 | ✓ |
| Retirement-year FICA = 0 (no wages) | 0 | 0 | ✓ |
| Non-working spouse: total SS = primary own + spousal floor ($54K) | 54,000 | 54,000 | ✓ |
| Low-earning spouse ($700/mo own): total SS top-up to floor ($43,200/yr) | 43,200 | 43,200 | ✓ |
| High-earning spouse ($1,200/mo own > floor): no spousal top-up, total $38,400 | 38,400 | 38,400 | ✓ |
| Spousal benefit = $0 before primary files (primary age 67, files at 70) | 0 | 0 | ✓ |
| Spousal benefit activates when primary files at 70 | 59,640 | 59,640 | ✓ |
| spousalTopUp field exists in projection year output | 1 | 1 | ✓ |
| spousalTopUp value is correct ($18,000 for non-working spouse) | 18,000 | 18,000 | ✓ |
| No spousal benefit when spouse not enabled | 24,000 | 24,000 | ✓ |
| Spousal benefit stable year-over-year with zero COLA | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| netWorth identical when all accounts have explicit rates | 1,121,977 | 1,121,977 | ✓ |
| preTax identical across nominalReturn values | 436,788 | 436,788 | ✓ |
| roth identical across nominalReturn values | 685,189 | 685,189 | ✓ |
| taxable identical across nominalReturn values | 0 | 0 | ✓ |
| accounts grow at their explicit rates (sanity) | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| MC netWorth identical despite nominalReturn 5 vs 15 | 744,144 | 744,144 | ✓ |
| MC preTax identical despite nominalReturn 5 vs 15 | 393,293 | 393,293 | ✓ |
| MC results differ from deterministic (sanity) | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| RMD on $0 balance = $0 | 0 | 0 | ✓ |
| RMD at age 110 (beyond table) uses fallback | 1 | 1 | ✓ |
| SS estimate with $0 income → $0 benefit | 0 | 0 | ✓ |
| calcTax on negative income → $0 | 1 | 1 | ✓ |
| IRMAA at exact $218K MFJ threshold → Tier 1 | 1 | 1 | ✓ |
| State tax with invalid code → $0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Under FRA: $20K earnings (below $24,480 limit) → $0 | 0 | 0 | ✓ |
| Under FRA: $50K earnings → $12,760 withheld | 12,760 | 12,760 | ✓ |
| Under FRA: $100K earnings, $16,800 benefit → capped at $16,800 | 16,800 | 16,800 | ✓ |
| At FRA (age 67, born 1960+): $200K earnings → $0 | 0 | 0 | ✓ |
| After FRA (age 70): $200K earnings → $0 | 0 | 0 | ✓ |
| Zero earnings → $0 | 0 | 0 | ✓ |
| FRA year (66.5): $70K earnings → $1,613 | 1,613 | 1,613 | ✓ |
| SSDI: exempt from earnings test | 0 | 0 | ✓ |
| Zero SS benefit → $0 | 0 | 0 | ✓ |
| Earnings at exact $24,480 limit → $0 | 0 | 0 | ✓ |
| Inflation indexing: 2030 under-FRA limit | 10,900 | 10,900 | ✓ |
| Integration: age 62 (working + SS) has withholding | 1 | 1 | ✓ |
| Integration: age 67 (FRA, retired) no withholding | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| No withholding control: claim 64, 0 months → $1600 | 1,600 | 1,600 | ✓ |
| Amy's example: claim 64, 18 months withheld → $1800 | 1,800 | 1,800 | ✓ |
| Partial credit: claim 64, 12 months → $1733 | 1,733 | 1,733 | ✓ |
| Over-credit cap: claim 64, 50 months → $2000 (capped) | 2,000 | 2,000 | ✓ |
| Beyond-36: claim 62, 12 months → $1500 | 1,500 | 1,500 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Single claim-64: age 64 ssWithheld = 19200 | 19,200 | 19,200 | ✓ |
| Single claim-64: age 67 ss = 24000 (post-ARF restored) | 24,000 | 24,000 | ✓ |
| Already-collecting: age 67 ss = 18000 (no ARF jump) | 18,000 | 18,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2026 baseline → $184,500 unchanged | 184,500 | 184,500 | ✓ |
| 2030 (4yr forward) → $212,400 | 212,400 | 212,400 | ✓ |
| 2036 (10yr forward) → $262,800 | 262,800 | 262,800 | ✓ |
| 2046 (20yr forward) → $374,400 | 374,400 | 374,400 | ✓ |
| $150-tie rounding → rounds UP per §430(b)(2) | 248,100 | 248,100 | ✓ |
| FICA at 2026 → identical to pre-fix (year-zero invariance) | 11,439 | 11,439 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Household of 1 base FPL = $15,650 | 15,650 | 15,650 | ✓ |
| Household of 2 base FPL = $21,150 | 21,150 | 21,150 | ✓ |
| Household of 3 base FPL = $26,650 | 26,650 | 26,650 | ✓ |
| Household of 4 base FPL = $32,150 | 32,150 | 32,150 | ✓ |
| Household of 5 base FPL = $37,650 | 37,650 | 37,650 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 150% FPL enhanced (2025) → 0% contribution, full subsidy | 0 | 0 | ✓ |
| 200% FPL enhanced (2025) → 2% applicable | 2 | 2 | ✓ |
| 300% FPL enhanced (2025) → 6% applicable | 6 | 6 | ✓ |
| 400% FPL enhanced (2025) → 8.5% applicable | 8.5 | 8.5 | ✓ |
| 500% FPL enhanced (2025) → still eligible (no cliff) | 1 | 1 | ✓ |
| Below 100% FPL → not eligible for subsidy | 0 | 0 | ✓ |
| 400%+ FPL original (2026) → no subsidy (cliff) | 0 | 0 | ✓ |
| 300% FPL original (2026) → 9.96% applicable | 9.96 | 9.96 | ✓ |
| 130% FPL original (2026) → 2.10% applicable | 2.1 | 2.1 | ✓ |
| Full subsidy at 150% FPL enhanced = benchmark premium | 20,000 | 20,000 | ✓ |
| 175% FPL enhanced → 1.0% applicable (interpolation) | 1 | 1 | ✓ |
| Couple at $44k expenses, 2026 original rules → positive subsidy | 1 | 1 | ✓ |
| Couple at $44k expenses, 2026 original rules → FPL ≈ 208% | 208 | 208 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Age 55 single base → ~$9,000 | 9,000 | 9,000 | ✓ |
| Age 60 couple > age 55 single | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| No actual plan arg → netPremium = benchmark-based (regression) | 3,807 | 3,807 | ✓ |
| actualPlanPremium = 0 → same as benchmark (sentinel) | 3,807 | 3,807 | ✓ |
| Gold plan above benchmark → net = plan − full credit | 5,807 | 5,807 | ✓ |
| Bronze plan below credit → net premium floored at $0 | 0 | 0 | ✓ |
| Bronze plan below credit → subsidy capped at plan premium | 7,000 | 7,000 | ✓ |
| Over-cliff ineligible with actual plan → net = actual plan | 7,000 | 7,000 | ✓ |
| Over-cliff ineligible, no actual plan → net = benchmark | 12,000 | 12,000 | ✓ |
| Below-100%-FPL ineligible with actual plan → net = actual plan | 7,000 | 7,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Pension starts at age 65 with $24,000/yr | 24,000 | 24,000 | ✓ |
| Pension COLA: $24K at 2% after 5yr ≈ $26,497 | 26,497 | 26,498 | ✓ |
| Taxable 100%: pensionTaxable = pension | 24,000 | 24,000 | ✓ |
| Taxable 60%: pensionTaxable ≈ 60% of pension | 14,400 | 14,400 | ✓ |
| Taxable 0%: pensionTaxable = 0 (fully exempt) | 0 | 0 | ✓ |
| Single-life: pension = 0 after pensioner dies | 0 | 0 | ✓ |
| Joint & 50%: survivor gets 50% of pension | 12,000 | 12,000 | ✓ |
| Joint & 75%: survivor gets 75% of pension | 18,000 | 18,000 | ✓ |
| Joint & 100%: survivor gets 100% of pension | 24,000 | 24,000 | ✓ |
| J&50% + 60% taxable: survivor taxable = 50%*60%*24000 = 7200 | 7,200 | 7,200 | ✓ |
| Migration shim: legacy pension scalar produces correct income | 18,000 | 18,000 | ✓ |
| Two primary pensions sum: 12000 + 18000 = 30000 | 30,000 | 30,000 | ✓ |
| Staggered start ages: only first pension active at age 66 | 12,000 | 12,000 | ✓ |
| Staggered start ages: both active at age 67 | 30,000 | 30,000 | ✓ |
| Mixed-owner two pensions: primary 12000 + spouse 8000 = 20000 | 20,000 | 20,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Integer startAge=65, cola=0 → full $24K at age 65 (regression) | 24,000 | 24,000 | ✓ |
| Integer startAge=65, cola=0 → full $24K at age 70 (regression) | 24,000 | 24,000 | ✓ |
| startAge=65.5 → 50% prorated ($12K) at age 65 | 12,000 | 12,000 | ✓ |
| startAge=65.5 → full $24K at age 66 | 24,000 | 24,000 | ✓ |
| startAge=65.25 → 75% prorated ($18K) at age 65 | 18,000 | 18,000 | ✓ |
| startAge=65.75 → 25% prorated ($6K) at age 65 | 6,000 | 6,000 | ✓ |
| startAge=65.5 → $0 at age 64 (before activation) | 0 | 0 | ✓ |
| Lifetime total: fractional pension = integer total - half-year | 12,000 | 12,000 | ✓ |
| Spouse startAge=65.5 → 50% prorated ($6K) at spouse age 65 | 6,000 | 6,000 | ✓ |
| COLA compounds with fractional penYrs (age 70, startAge 65.5, 2% COLA) | 26,237 | 26,237 | ✓ |
| taxablePercent applies post-proration (60% of $12K = $7,200) | 7,200 | 7,200 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Other income shows in projection at age 65 | 30,000 | 30,000 | ✓ |
| $30K additional income increases lifetime tax | 1 | 1 | ✓ |
| Tax increase is meaningful (>$10K lifetime) | 1 | 1 | ✓ |
| Default window: other income at retirement age | 30,000 | 30,000 | ✓ |
| Default window: other income at end age | 30,000 | 30,000 | ✓ |
| Late start (70): no other income at 65 | 0 | 0 | ✓ |
| Late start (70): no other income at 69 | 0 | 0 | ✓ |
| Late start (70): other income begins at 70 | 30,000 | 30,000 | ✓ |
| Early end (72): other income at 72 | 30,000 | 30,000 | ✓ |
| Early end (72): no other income at 73 | 0 | 0 | ✓ |
| Pre-retirement start (60): other income while working | 30,000 | 30,000 | ✓ |
| Window end (75): last year of other income | 30,000 | 30,000 | ✓ |
| After window (76): no other income | 0 | 0 | ✓ |
| $100K additional income raises IRMAA surcharges | 1 | 1 | ✓ |
| FillBracket: $50K other income reduces Roth conversion amount | 1 | 1 | ✓ |
| FillBracket: conversion reduced by ~$50K with other income | 1 | 0 | ✓ |
| SmartFill: $50K other income reduces Roth conversion | 1 | 1 | ✓ |
| TargetMAGI: $50K other income reduces Roth conversion | 1 | 1 | ✓ |
| Stuck state: empty incomeStreams + legacy additionalIncome → other = 0 | 0 | 0 | ✓ |
| Legacy plan: incomeStreams undefined + additionalIncome → other = 5000 | 5,000 | 5,000 | ✓ |
| Populated incomeStreams: additionalIncome ignored, other = stream amount | 12,000 | 12,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Term premiums active before expiry (age 74) | 2,400 | 2,400 | ✓ |
| Term premiums stop at expiresAge (age 75) | 0 | 0 | ✓ |
| Term: no benefit if insured outlives term (death 80, expires 75) | 0 | 0 | ✓ |
| Term: benefit pays when death before expiry (death 72, expires 75) | 500,000 | 500,000 | ✓ |
| Permanent: premiums active before death (age 84) | 5,000 | 5,000 | ✓ |
| Permanent: premiums stop at death (age 85) | 0 | 0 | ✓ |
| Permanent: benefit pays at death age 85 | 500,000 | 500,000 | ✓ |
| Death benefit does NOT inflate MAGI by benefit amount (IRC §101) | 1 | 1 | ✓ |
| Death benefit increases brokerage balance | 1 | 1 | ✓ |
| No benefit when no death age configured | 0 | 0 | ✓ |
| Multiple policies: both pay out at death | 750,000 | 750,000 | ✓ |
| Spouse policy: benefit triggers at spouse death | 300,000 | 300,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| REGS object exists | 1 | 1 | ✓ |
| REGS.version is 2025 | 1 | 1 | ✓ |
| REGS has 5 data sections | 5 | 5 | ✓ |
| federal.brackets_mfj has 7 brackets | 7 | 7 | ✓ |
| federal.brackets_single has 7 brackets | 7 | 7 | ✓ |
| irmaa.brackets_mfj has 6 tiers | 6 | 6 | ✓ |
| aca.pct_enhanced has 6 tiers | 6 | 6 | ✓ |
| TAX_BRACKETS_MFJ === REGS.federal.brackets_mfj | 1 | 1 | ✓ |
| IRMAA_BRACKETS_MFJ === REGS.irmaa.brackets_mfj | 1 | 1 | ✓ |
| ACA_PCT_ENHANCED === REGS.aca.pct_enhanced | 1 | 1 | ✓ |
| SS_WAGE_BASE matches REGS | 184,500 | 184,500 | ✓ |
| CONTRIB_401K matches REGS | 24,500 | 24,500 | ✓ |
| STD_DEDUCTION matches REGS | 32,200 | 32,200 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| HSA contribution reduces federal taxable income | 1 | 1 | ✓ |
| HSA contribution does NOT reduce CA state income | 6,697 | 6,697 | ✓ |
| HSA contribution DOES reduce OR state income | 1 | 1 | ✓ |
| HSA balance grows at configured rate tax-free | 53,000 | 53,000 | ✓ |
| HSA medical withdrawal at age 60: tax-free | 0 | 0 | ✓ |
| HSA non-medical withdrawal at age 60: taxed + 20% penalty | 1 | 1 | ✓ |
| HSA withdrawal at age 67 (100% medical): tax-free | 0 | 0 | ✓ |
| HSA medical withdrawal at 67: zero MAGI impact | 0 | 0 | ✓ |
| HSA withdrawal at age 67 (50% medical): 50% taxed | 16,218 | 16,224 | ✓ |
| HSA: no RMD generated at any age | 0 | 0 | ✓ |
| Contribution limits enforced: self, age 56 = $5,400 max | 5,400 | 5,400 | ✓ |
| Contribution limits enforced: family, age 58 = $9,750 max | 9,750 | 9,750 | ✓ |
| Contributions stop at hsaContributionStopAge | 1 | 1 | ✓ |
| SSDI early Medicare (age 62) stops HSA contributions | 1 | 1 | ✓ |
| ACA MAGI reduced by HSA contribution | 1 | 1 | ✓ |
| Net worth includes HSA balance | 195,000 | 195,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| birthMonth=6 → healthcare ≈ full × 6/12 in transition year | 6,000 | 6,000 | ✓ |
| birthMonth=6 → medicareCost ≈ full × 6/12 in transition year | 1,218 | 1,218 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| HSA self year 5, age 60, 3% inflation → $6,101 | 6,101 | 6,101 | ✓ |
| HSA family year 9, age 64, 3% inflation → $12,417 | 12,417 | 12,417 | ✓ |
| HSA year-zero invariance: self age 56 = $5,400 (regression of Test 10) | 5,400 | 5,400 | ✓ |
| IRA cumulative roth at year 5 (age 50 vs 44, 3% inflation) → delta $6,600 | 6,600 | 6,600 | ✓ |
| IRA year-zero invariance: roth[0] delta = $1,100 (age 50 vs 44) | 1,100 | 1,100 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| $0 balance returns null (truthy check) | 1 | 1 | ✓ |
| $1M at 0% return, 22% bracket → net ~$780K | 780,000 | 780,000 | ✓ |
| Balance fully depleted after 10 years | 0 | 0 | ✓ |
| Year 1 dist at 0% return = balance/10 | 100,000 | 100,000 | ✓ |
| Higher bracket (24%) yields less net than 22% | 1 | 1 | ✓ |
| 7% growth: total distributed > initial balance | 1 | 1 | ✓ |
| Post-2019: $100K, 10 yrs left → $10,000 | 10,000 | 10,000 | ✓ |
| Post-2019: $100K, 1 yr left → full balance | 100,000 | 100,000 | ✓ |
| Post-2019: $0 balance → $0 | 0 | 0 | ✓ |
| Post-2019: $250K, 5 yrs left → $50,000 | 50,000 | 50,000 | ✓ |
| Pre-2020 stretch: age 50 yr 1, $1M → $27,624 | 27,624.309 | 27,624 | ✓ |
| Pre-2020 stretch: age 50 yr 5 (divisor 32.2), $1M → $31,056 | 31,055.901 | 31,056 | ✓ |
| Pre-2020 stretch: age 65 yr 1, $500K → $21,834 | 21,834.061 | 21,834 | ✓ |
| Pre-2020 stretch: divisor floor at 1.0 (divisor would go negative) | 500,000 | 500,000 | ✓ |
| Distribution never exceeds balance | 1,000 | 1,000 | ✓ |
| Unknown regime returns 0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Year 0: regular pretax balance = 500K (untouched) | 500,000 | 500,000 | ✓ |
| Year 0: inherited pretax = 450K after first distribution | 450,000 | 450,000 | ✓ |
| Year 0: inherited distribution = 50K | 50,000 | 50,000 | ✓ |
| Year 9 (age 69): inherited balance = 0 (fully distributed) | 0 | 0 | ✓ |
| Year 10 (age 70): inherited dist = 0 (nothing left) | 0 | 0 | ✓ |
| Net worth = sum of all buckets including inherited | 1,266,180 | 1,266,180 | ✓ |
| Inherited pretax distributions increase lifetime taxes | 1 | 1 | ✓ |
| Inherited distributions preserve pretax (less drawdown needed) | 1 | 1 | ✓ |
| Roth conversion pulls from pretax only (not inherited) | 460,000 | 460,000 | ✓ |
| Inherited balance unchanged by Roth conversion (still 450K) | 450,000 | 450,000 | ✓ |
| No standard RMD when only inherited pretax exists | 0 | 0 | ✓ |
| Inherited Roth has lower lifetime tax than inherited traditional | 1 | 1 | ✓ |
| Inherited Roth depleted by year 9 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Yr 1: age 55, $500K → ~$15,823 | 15,823 | 15,823 | ✓ |
| Stretch balance adds to netWorth yr 1 | 1 | 1 | ✓ |
| Inherited Roth stretch: tax-free distributions never raise tax | 1 | 1 | ✓ |
| Traditional stretch yr 1 inheritedDist recorded | 12,658 | 12,658 | ✓ |
| No regime field: v5.4 pooled behavior preserved | 9,091 | 9,091 | ✓ |
| Mixed stretch + post-2019: both distribute | 29,252 | 29,252 | ✓ |
| Stretch balance decreases year-over-year at 0% return | 1 | 1 | ✓ |
| EDB Disabled: yr 1 uses stretch math (100K / 31.6 age 55) | 3,165 | 3,165 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Two post-2019 with different inheritedYear: independent schedules | 25,758 | 25,758 | ✓ |
| Earlier-inherited account depletes first | 1 | 1 | ✓ |
| Per-account distributionTiming respected (Roth) | 9,091 | 9,091 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Disabled: zero harvested | 1 | 1 | ✓ |
| Enabled with basis: harvests in low-income year | 1 | 1 | ✓ |
| No unrealized gains: zero harvested | 1 | 0 | ✓ |
| Harvested amount ≤ unrealized gains | 1 | 1 | ✓ |
| SS-feedback: harvesting raises fedTax via SS provisional income | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| earnedFederalTaxable exposed on year-row | 1 | 1 | ✓ |
| taxableWd exposed on year-row | 1 | 1 | ✓ |
| taxableRothConv exposed on year-row | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Disabled: zero offset | 1 | 1 | ✓ |
| $5K losses: offset applied | 1 | 1 | ✓ |
| $3K ordinary offset when minimal CG | 1 | 1 | ✓ |
| Carry-forward accumulates across years | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Disabled: uses standard deduction | 1 | 1 | ✓ |
| SALT $10K + charity $25K beats standard for single filer | 1 | 1 | ✓ |
| Low SALT, no charity: standard wins for MFJ senior | 1 | 1 | ✓ |
| Medical 7.5% AGI floor applied correctly | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 2025 base cap $40,000 MFJ/Single | 40,000 | 40,000 | ✓ |
| 2026 cap with 1% growth: $40,400 | 40,400 | 40,400 | ✓ |
| 2030 reversion to $10,000 TCJA baseline | 10,000 | 10,000 | ✓ |
| 2025 phase-down at MAGI $550K: cap $25,000 | 25,000 | 25,000 | ✓ |
| 2025 phase-down floor at MAGI $700K: cap $10,000 | 10,000 | 10,000 | ✓ |
| 2026 phase-down threshold also grows: MAGI $505K → full cap | 40,400 | 40,400 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Disabled: charity same every year | 1 | 1 | ✓ |
| Every-2yr: bunch year has 2x charity | 1 | 1 | ✓ |
| Every-2yr: off year has zero charity | 1 | 1 | ✓ |
| Lifetime charity total unchanged vs no bunching | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Disabled: no QLAC payout or balance | 1 | 1 | ✓ |
| Purchase reduces pre-tax balance at purchase age | 1 | 1 | ✓ |
| RMD lower with QLAC than without | 1 | 1 | ✓ |
| Payout starts at payoutStartAge as ordinary income | 1 | 1 | ✓ |
| 2026 QLAC limit: $210,000 | 210,000 | 210,000 | ✓ |
| Engine caps purchase at QLAC_LIMIT when user requests more | 210,000 | 210,000 | ✓ |
| Dual disabled: per-owner balances both 0 | 1 | 1 | ✓ |
| Primary-only QLAC: primary balance = 200K, spouse balance = 0 | 200,000 | 200,000 | ✓ |
| Primary-only QLAC: spouse balance unchanged | 0 | 0 | ✓ |
| Dual QLAC: per-owner balances both = 200K at purchase age | 400,000 | 400,000 | ✓ |
| Dual QLAC: qlacBalance backward-compat = primary + spouse | 0 | 0 | ✓ |
| Dual QLAC: netWorth includes both owners QLAC balance | 1,604,857 | 1,604,857 | ✓ |
| Spouse-only QLAC: payout active when spouse reaches payoutStartAge | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Not-passed birthday, retireMonth before birthMonth: partial in yr0 | 60,000 | 60,000 | ✓ |
| Not-passed birthday, retireMonth before birthMonth: zero in yr1 | 0 | 0 | ✓ |
| Passed birthday, retireMonth after birthMonth: partial in yr0 | 60,000 | 60,000 | ✓ |
| Passed birthday, retireMonth after birthMonth: zero in yr1 | 0 | 0 | ✓ |
| Passed birthday, future retireMonth before birthMonth: full in yr5 | 120,000 | 120,000 | ✓ |
| Passed birthday, future retireMonth before birthMonth: partial in yr6 | 10,000 | 10,000 | ✓ |
| Passed birthday, future retireMonth before birthMonth: zero in yr7 | 0 | 0 | ✓ |
| Not-passed birthday, retireMonth after birthMonth: partial in yr0 | 100,000 | 100,000 | ✓ |
| Not-passed birthday, retireMonth after birthMonth: zero in yr1 | 0 | 0 | ✓ |
| Spouse not-passed birthday, retireMonth before birthMonth: spouse partial in yr0 | 60,000 | 60,000 | ✓ |
| Spouse not-passed birthday, retireMonth before birthMonth: spouse zero in yr1 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Before primary retires: conversion caps at IRA (convertible) balance only | 50,000 | 50,000 | ✓ |
| After convertible pool drained, conversions stop even with gated balance remaining | 0 | 0 | ✓ |
| At primaryRetires age, gated 401k unlocks and permits conversion again | 1 | 1 | ✓ |
| Without gate, conversions continue at age 62 from full pretax pool | 100,000 | 100,000 | ✓ |
| Absent conversionGate field = always convertible (backward compat) | 100,000 | 100,000 | ✓ |
| Spouse-gated account: still locked when spouse < spouseRetirementAge | 0 | 0 | ✓ |
| Spouse-gated account: unlocks when spouse reaches spouseRetirementAge | 1 | 1 | ✓ |
| spouseRetires gate with spouseEnabled=false coerces to always-convertible | 50,000 | 50,000 | ✓ |
| specificAge gate: still locked before the specified age | 0 | 0 | ✓ |
| specificAge gate: unlocks at the specified primary age | 1 | 1 | ✓ |
| Account identity holds with gated tranches in pool | 12,641 | 12,641 | ✓ |
| RMDs draw proportionally from full pool including gated tranches | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Fix path AGI matches gate path AGI at Y3 (spouse age 60, working) | 238,000 | 238,000 | ✓ |
| Fix path rothConv matches gate path rothConv at Y3 | 163,919 | 163,919 | ✓ |
| Fix path fedTax matches gate path fedTax at Y3 | 33,076 | 33,076 | ✓ |
| Fix path earned matches gate path earned at Y3 | 97,631 | 97,631 | ✓ |
| Pre-retirement year Y1: both paths produce same AGI (gate runs in both) | 70,290 | 70,290 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Spouse RMD fires at spAge 73 (projection yr 4) | 1 | 1 | ✓ |
| Primary RMD = 0 at primary age 69 (below RMD start) | 0 | 0 | ✓ |
| Total rmd = rmdSpouse when only spouse at RMD age | 22,487 | 22,487 | ✓ |
| Spouse RMD amount ≈ balance ÷ 26.5 at age 73 | 22,487 | 22,487 | ✓ |
| Primary RMD fires at primary age 75 (projection yr 10) | 1 | 1 | ✓ |
| Spouse RMD still firing at spAge 79 (yr 10) | 1 | 1 | ✓ |
| Total rmd = rmdPrimary + rmdSpouse when both at RMD age | 61,814 | 61,814 | ✓ |
| Invariant: preTax = preTaxPrimary + preTaxSpouse every year | 1 | 1 | ✓ |
| Opt-out: no owner:spouse account → preTaxSpouse stays 0 | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Primary bucket grows at 5% (per-owner rate) | 638,141 | 638,141 | ✓ |
| Spouse bucket grows at 7% (per-owner rate) | 701,276 | 701,276 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Primary death → spouse absorbs pool (preTaxPrimary = 0) | 0 | 0 | ✓ |
| Spouse death → primary absorbs pool (preTaxSpouse = 0) | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Year 1 interest: $200K @ 6% / $1,199.10/mo | 11,933 | 11,933 | ✓ |
| Year 1 principal: $200K @ 6% / $1,199.10/mo | 2,456 | 2,456 | ✓ |
| Year 1 ending balance: $200K @ 6% / $1,199.10/mo | 197,544 | 197,544 | ✓ |
| Year 1 total paid: 12 × $1,199.10/mo | 14,389 | 14,389 | ✓ |
| $200K @ 6% / $1,199.10/mo pays off in 30 years | 30 | 31 | ✓ |
| Zero-rate loan: $12K @ 0% / $1,000/mo — no interest | 0 | 0 | ✓ |
| Zero-rate loan: $12K @ 0% / $1,000/mo — paid off in year 1 | 1 | 1 | ✓ |
| Zero balance: no payment, no interest | 0 | 0 | ✓ |
| Payment < monthly interest: balance unchanged | 200,000 | 200,000 | ✓ |
| Engine integration: year-1 interest matches helper | 11,933 | 11,933 | ✓ |
| Engine integration: year-1 payments matches helper | 14,389 | 14,389 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| No relocation params: CA tax non-zero pre-retirement (age 60) | 1 | 1 | ✓ |
| Move at 70: pre-move year (age 65) uses original state | 1 | 1 | ✓ |
| Move at 70: post-move year (age 75) uses target state | 0 | 0 | ✓ |
| Move at 70: cutover year (age 70 itself) uses target state | 0 | 0 | ✓ |
| Move at currentAge: target state for entire projection | 0 | 0 | ✓ |
| Move beyond endAge: original state for entire projection | 1 | 1 | ✓ |
| Pre-retirement move at 55: working year (age 60) uses target state | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Empty phases array: identical to absent phases (regression) | 87,557 | 87,557 | ✓ |
| Single phase = single-target (full-window 12% phase) | 1,407 | 1,407 | ✓ |
| Two-phase: 24% phase produces larger conv than 12% phase | 1 | 1 | ✓ |
| Gap year (age 68): zero conversion | 0 | 0 | ✓ |
| Phase boundary: endAge is inclusive (age 67 is in phase 1) | 1 | 1 | ✓ |
| Outer rc.startAge gates phases: age 60 outside outer window → 0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Legacy type:cash → type:taxable | 1 | 1 | ✓ |
| Legacy type:cash → subtype:cash | 1 | 1 | ✓ |
| Legacy type:cash preserves balance | 65,000 | 65,000 | ✓ |
| Legacy type:cash preserves returnRate | 4.5 | 4.5 | ✓ |
| Canonical taxable (no subtype) unchanged | 1 | 1 | ✓ |
| Type pretax passes through unchanged | 1 | 1 | ✓ |
| Type roth passes through unchanged | 1 | 1 | ✓ |
| Type hsa passes through unchanged | 1 | 1 | ✓ |
| Type inherited-pretax passes through unchanged | 1 | 1 | ✓ |
| Type inherited-roth passes through unchanged | 1 | 1 | ✓ |
| canonicalizeAccount is idempotent | 1 | 1 | ✓ |
| Legacy type:cash without returnRate → default 4 | 4 | 4 | ✓ |
| Legacy cash account → engine cash bucket > 0 in year 0 | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Pre-tax overridden to nominalReturn | 7 | 7 | ✓ |
| Roth overridden to nominalReturn | 7 | 7 | ✓ |
| Taxable-investment overridden | 7 | 7 | ✓ |
| Cash subtype keeps its rate | 4.5 | 4.5 | ✓ |
| HSA overridden to nominalReturn | 7 | 7 | ✓ |
| Input array not mutated | 5 | 5 | ✓ |
| blendedAccountsForOptimizer is idempotent | 7 | 7 | ✓ |
| Integration: real Roth 10% > blended Roth 7% by year 30 | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| pre-tax 5% vs Roth 9% → shouldWarn:true | 1 | 1 | ✓ |
| pre-tax 5% vs Roth 9% → divergence == 4 | 4 | 4 | ✓ |
| pre-tax 6% vs Roth 7.5% (1.5pp) → shouldWarn:false | 0 | 0 | ✓ |
| Exactly 2.0pp → shouldWarn:true | 1 | 1 | ✓ |
| Roth < $10K → shouldWarn:false | 0 | 0 | ✓ |
| No Roth → shouldWarn:false | 0 | 0 | ✓ |
| Multi-account pre-tax: weighted avg 5.4% | 5.4 | 5.4 | ✓ |
| Multi-account divergence 2.6pp → shouldWarn:true | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Single retiree FL: tax > 0 in year 1 | 1 | 1 | ✓ |
| Single retiree FL: withdrawal exceeds raw expense gap | 1 | 1 | ✓ |
| Couple MFJ GA: state tax > 0 with $50K taxable pension | 1 | 1 | ✓ |
| Couple MFJ GA: withdrawal covers fed+state tax bill | 1 | 1 | ✓ |
| Stable-income retiree: iteration converges from year 1+ (warm-start) | 1 | 1 | ✓ |
| Year record exposes taxConvergenceFlag as boolean | 1 | 1 | ✓ |
| NIIT triggers for high-MAGI single via iteration | 1 | 1 | ✓ |
| SS taxation propagates through iteration (couple MFJ) | 1 | 1 | ✓ |
| wdOrder honored: roth untouched when earlier sources suffice | 1 | 1 | ✓ |
| Pre-retirement: taxConvergenceFlag stays false (no iteration) | 1 | 1 | ✓ |
| Pre-retirement: withdrawal stays 0 (no fillGap path) | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Age 58 (yr 3 post-conv): $5K Roth wd → ~$500 penalty (iter cascade adds slight overshoot) | 530 | 556 | ✓ |
| Age 60: Roth wd → no penalty (age >59.5 exemption) | 0 | 0 | ✓ |
| Age 61: Roth wd → no penalty (age + 5-yr both clear) | 0 | 0 | ✓ |
| Two conversions, $15K wd at age 54 → penalty oldest-first ≈ $1500-$1700 | 1,600 | 1,611 | ✓ |
| Age 56 ($5K wd, 1yr post-conv) → ~$500 penalty | 530 | 500 | ✓ |
| Age 60 ($10K wd, 5yr post-conv but >59.5) → no penalty | 0 | 0 | ✓ |
| No conversions, age 55 Roth wd → no penalty (empty tranches) | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Default even-spread: year 2026 has non-zero inherited Roth dist | 1 | 1 | ✓ |
| Back-load: zero distribution in 2026 (year 4 of 10) | 1 | 1 | ✓ |
| Back-load: zero distribution in 2032 (year 9 of 10) | 1 | 1 | ✓ |
| Back-load: full balance distributed in deadline year 2033 | 1 | 1 | ✓ |
| Back-load total distributions > even-spread total (extra compounding) | 1 | 1 | ✓ |
| Back-load: inherited Roth balance = 0 after year 10 | 1 | 1 | ✓ |
| Back-load: lifetime federal tax not increased by inherited Roth timing | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| 4% × $1M brokerage = $40K dividend income (year 0) | 40,000 | 40,000 | ✓ |
| $40K brokerage QD reflected in MAGI | 1 | 1 | ✓ |
| Ordinary dividend yield produces higher fed tax than qualified | 1 | 1 | ✓ |
| Account without dividendYield: zero dividend income | 0 | 0 | ✓ |
| Multi-account: dividends sum correctly | 40,000 | 40,000 | ✓ |
| Mixed qualified + ordinary: brokerageDividendIncome sums both | 40,000 | 40,000 | ✓ |
| Untouched brokerage: dividend income grows with balance over 10 years | 1 | 1 | ✓ |
| Drained brokerage: dividend income shrinks as balance is withdrawn | 1 | 1 | ✓ |
| No brokerage accounts: zero div income, no NaN | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| $50K QD stream raises lifetime fed tax | 1 | 1 | ✓ |
| $50K QD stream reflected in MAGI | 1 | 1 | ✓ |
| High-MAGI single filer: NIIT > 0 on QD | 1 | 1 | ✓ |
| $30K QD alone < $48,350 single 0% LTCG threshold → $0 fed tax | 0 | 0 | ✓ |
| F-037: high-MAGI single with non-QD stream pays NIIT > 0 | 1 | 1 | ✓ |
| F-037: nonqualifiedDividends field exposed in year output | 250,000 | 250,000 | ✓ |
| F-037: high-MAGI single with ordinary brokerage div pays NIIT > 0 | 1 | 1 | ✓ |
| F-037: $250K QD and $250K non-QD produce equal NIIT (same NII base) | 2,921 | 2,921 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| p10/median/p90 returned and equal-length | 1 | 1 | ✓ |
| p10 <= median <= p90 at EVERY age (the fan-chart property) | 1 | 1 | ✓ |
| Envelope entry has age/year/retired/netWorth fields | 1 | 1 | ✓ |
| Final-year p10 < final-year p90 (non-degenerate) | 1 | 1 | ✓ |
| Final-year envelope spread > year-0 spread (variance grows) | 1 | 1 | ✓ |
| successRate is integer 0-100 | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| born March 1961, projYear 2026 → year-end age 65 | 65 | 65 | ✓ |
| born December 1961, projYear 2026 → year-end age 65 | 65 | 65 | ✓ |
| born January 1962, projYear 2026 → year-end age 64 | 64 | 64 | ✓ |
| born March 1961, projYear 2025 → year-end age 64 | 64 | 64 | ✓ |
| born March 1961, projYear 2030 → year-end age 69 | 69 | 69 | ✓ |
| born 1961, projYear 1961 → age 0 (newborn) | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| F-213: obbbSeniorDeductionFor returns 6000 in 2025 | 6,000 | 6,000 | ✓ |
| F-213: obbbSeniorDeductionFor returns 6000 in 2026 | 6,000 | 6,000 | ✓ |
| F-213: obbbSeniorDeductionFor returns 6000 in 2027 | 6,000 | 6,000 | ✓ |
| F-213: obbbSeniorDeductionFor returns 6000 in 2028 | 6,000 | 6,000 | ✓ |
| F-213: obbbSeniorDeductionFor returns 0 in 2024 (pre-window) | 0 | 0 | ✓ |
| F-213: obbbSeniorDeductionFor returns 0 in 2029 (post-sunset) | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| F-213: obbbPhaseoutThresholdFor MFJ returns 150000 in 2025 | 150,000 | 150,000 | ✓ |
| F-213: obbbPhaseoutThresholdFor MFJ returns 150000 in 2026 | 150,000 | 150,000 | ✓ |
| F-213: obbbPhaseoutThresholdFor MFJ returns 150000 in 2027 | 150,000 | 150,000 | ✓ |
| F-213: obbbPhaseoutThresholdFor MFJ returns 150000 in 2028 | 150,000 | 150,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Trad-only: AGI = $100k+$80k − $23.5k(Trad) − $15k(Trad) = $141,500 | 141,500 | 141,500 | ✓ |
| Roth-only: AGI = $100k+$80k = $180,000 (no Box-1 reduction) | 180,000 | 180,000 | ✓ |
| Mixed P-Roth/S-Trad: AGI = $100k + ($80k − $15k) = $165,000 | 165,000 | 165,000 | ✓ |
| Mixed P-Trad/S-Roth: AGI = ($100k − $23.5k) + $80k = $156,500 | 156,500 | 156,500 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Legacy default: rmd > 0 in age-74 row | 1 | 1 | ✓ |
| Legacy default: rmdReinvested > 0 (cash funds gap, RMD reinvests) | 1 | 1 | ✓ |
| Legacy default: withdrawalForSpending + rmdReinvested === withdrawal | 127,565 | 127,565 | ✓ |
| Legacy default: rmdReinvested ≈ rmd (entire RMD reinvested when cash funds gap) | 78,431 | 78,431 | ✓ |
| spendFirst Case A (RMD < gap): rmdReinvested === 0 | 0 | 0 | ✓ |
| spendFirst Case A: withdrawalForSpending + rmdReinvested === withdrawal | 42,435 | 42,435 | ✓ |
| spendFirst Case A: withdrawalForSpending >= rmd (RMD fully went to spending) | 1 | 1 | ✓ |
| spendFirst Case B (RMD > gap): rmdReinvested > 0 AND < rmd | 1 | 1 | ✓ |
| spendFirst Case B: withdrawalForSpending + rmdReinvested === withdrawal | 78,431 | 78,431 | ✓ |
| spendFirst Case B: rmdReinvested === rmd − (gap-from-rmd) | 29,297 | 29,297 | ✓ |
| cash dest: cash[cash-dest] − cash[brok-dest] === rmdReinvested | 1 | 1 | ✓ |
| cash dest: taxableInv[brok-dest] − taxableInv[cash-dest] === rmdReinvested | 1 | 1 | ✓ |
| cash dest: total cash + brokerage equals brokerage-dest equivalent | 1 | 1 | ✓ |
| invariant: withdrawalForSpending + rmdReinvested === withdrawal in EVERY year | 1 | 1 | ✓ |
| invariant: no violating year in projection (loop diagnostic) | -1 | -1 | ✓ |
| migration equivalence: missing rmdHandling === explicit legacy default (withdrawal[0]) | 127,565 | 127,565 | ✓ |
| migration equivalence: missing rmdHandling === explicit legacy default (rmdReinvested[0]) | 78,431 | 78,431 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| ACA primary older: healthcare > 0 at primary age 67 (spouse 62) | 1 | 1 | ✓ |
| ACA primary older: healthcarePhase = split at primary 67 | 1 | 1 | ✓ |
| ACA primary older: healthcarePhase = couple at primary 63 | 1 | 1 | ✓ |
| ACA primary older: healthcare = 0 at primary 71 (both on Medicare) | 0 | 0 | ✓ |
| ACA primary older: enrollees = 2 at primary 63, 1 at primary 67 | 1 | 1 | ✓ |
| ACA split benchmark < couple benchmark | 1 | 1 | ✓ |
| ACA spouse older: healthcare > 0 at primary 62 (split phase) | 1 | 1 | ✓ |
| ACA spouse older: healthcarePhase = couple at primary 58 | 1 | 1 | ✓ |
| Same-age couple regression: healthcare = 0 at 66 (both on Medicare at 65) | 0 | 0 | ✓ |
| Single filer regression: healthcare = 0 at 66 | 0 | 0 | ✓ |
| SSDI primary: healthcare > 0 at primary 60 (spouse still pre-Medicare) | 1 | 1 | ✓ |
| SSDI primary: healthcarePhase = split at primary 60 | 1 | 1 | ✓ |
| Retiree plan split: cost at 67 < cost at 63 (single < couple) | 1 | 1 | ✓ |
| Retiree plan fallback ratio: split cost ≈ annualCost × 0.5 × inflation | 9,850 | 9,850 | ✓ |
| spouse_plan: primary on Medicare + spouse still working → $0 | 0 | 0 | ✓ |
| survivor: healthcare active for younger spouse before their Medicare | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Clarks age 64: per-source sum == withdrawal | 0 | 0 | ✓ |
| Clarks age 64: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 65: per-source sum == withdrawal | 19,807 | 19,807 | ✓ |
| Clarks age 65: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 66: per-source sum == withdrawal | 21,174 | 21,174 | ✓ |
| Clarks age 66: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 67: per-source sum == withdrawal | 59,946 | 59,946 | ✓ |
| Clarks age 67: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 68: per-source sum == withdrawal | 65,166 | 65,166 | ✓ |
| Clarks age 68: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 69: per-source sum == withdrawal | 42,538 | 42,538 | ✓ |
| Clarks age 69: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 70: per-source sum == withdrawal | 44,857 | 44,857 | ✓ |
| Clarks age 70: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 71: per-source sum == withdrawal | 47,254 | 47,254 | ✓ |
| Clarks age 71: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 72: per-source sum == withdrawal | 49,758 | 49,758 | ✓ |
| Clarks age 72: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 73: per-source sum == withdrawal | 52,337 | 52,337 | ✓ |
| Clarks age 73: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 74: per-source sum == withdrawal | 55,040 | 55,040 | ✓ |
| Clarks age 74: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Clarks age 75: per-source sum == withdrawal | 57,848 | 57,848 | ✓ |
| Clarks age 75: wdPretaxRMD == rmd - qcd | 52,218 | 52,218 | ✓ |
| Clarks age 76: per-source sum == withdrawal | 60,750 | 60,750 | ✓ |
| Clarks age 76: wdPretaxRMD == rmd - qcd | 55,384 | 55,384 | ✓ |
| Clarks age 77: per-source sum == withdrawal | 63,787 | 63,787 | ✓ |
| Clarks age 77: wdPretaxRMD == rmd - qcd | 58,492 | 58,492 | ✓ |
| Clarks age 78: per-source sum == withdrawal | 66,936 | 66,936 | ✓ |
| Clarks age 78: wdPretaxRMD == rmd - qcd | 62,045 | 62,045 | ✓ |
| Clarks age 79: per-source sum == withdrawal | 70,201 | 70,201 | ✓ |
| Clarks age 79: wdPretaxRMD == rmd - qcd | 65,825 | 65,825 | ✓ |
| Clarks age 80: per-source sum == withdrawal | 73,608 | 73,608 | ✓ |
| Clarks age 80: wdPretaxRMD == rmd - qcd | 69,853 | 69,853 | ✓ |
| Clarks age 81: per-source sum == withdrawal | 76,856 | 76,856 | ✓ |
| Clarks age 81: wdPretaxRMD == rmd - qcd | 73,765 | 73,765 | ✓ |
| Clarks age 82: per-source sum == withdrawal | 80,182 | 80,182 | ✓ |
| Clarks age 82: wdPretaxRMD == rmd - qcd | 78,323 | 78,323 | ✓ |
| Clarks age 83: per-source sum == withdrawal | 83,649 | 83,649 | ✓ |
| Clarks age 83: wdPretaxRMD == rmd - qcd | 82,746 | 82,746 | ✓ |
| Clarks age 84: per-source sum == withdrawal | 87,954 | 87,954 | ✓ |
| Clarks age 84: wdPretaxRMD == rmd - qcd | 87,954 | 87,954 | ✓ |
| Clarks age 85: per-source sum == withdrawal | 93,605 | 93,605 | ✓ |
| Clarks age 85: wdPretaxRMD == rmd - qcd | 92,934 | 92,934 | ✓ |
| Clarks age 86: per-source sum == withdrawal | 100,700 | 100,700 | ✓ |
| Clarks age 86: wdPretaxRMD == rmd - qcd | 98,131 | 98,131 | ✓ |
| Clarks age 87: per-source sum == withdrawal | 109,386 | 109,386 | ✓ |
| Clarks age 87: wdPretaxRMD == rmd - qcd | 103,542 | 103,542 | ✓ |
| Clarks age 88: per-source sum == withdrawal | 119,022 | 119,022 | ✓ |
| Clarks age 88: wdPretaxRMD == rmd - qcd | 108,364 | 108,364 | ✓ |
| Clarks age 89: per-source sum == withdrawal | 130,576 | 130,576 | ✓ |
| Clarks age 89: wdPretaxRMD == rmd - qcd | 114,151 | 114,151 | ✓ |
| Clarks age 90: per-source sum == withdrawal | 143,101 | 143,101 | ✓ |
| Clarks age 90: wdPretaxRMD == rmd - qcd | 119,139 | 119,139 | ✓ |
| Clarks age 91: per-source sum == withdrawal | 156,635 | 156,635 | ✓ |
| Clarks age 91: wdPretaxRMD == rmd - qcd | 124,153 | 124,153 | ✓ |
| Clarks age 92: per-source sum == withdrawal | 171,075 | 171,075 | ✓ |
| Clarks age 92: wdPretaxRMD == rmd - qcd | 129,153 | 129,153 | ✓ |
| Clarks age 93: per-source sum == withdrawal | 186,250 | 186,250 | ✓ |
| Clarks age 93: wdPretaxRMD == rmd - qcd | 134,089 | 134,089 | ✓ |
| Clarks age 94: per-source sum == withdrawal | 200,453 | 200,453 | ✓ |
| Clarks age 94: wdPretaxRMD == rmd - qcd | 137,434 | 137,434 | ✓ |
| Clarks age 95: per-source sum == withdrawal | 213,238 | 213,238 | ✓ |
| Clarks age 95: wdPretaxRMD == rmd - qcd | 140,446 | 140,446 | ✓ |
| Lees age 59: per-source sum == withdrawal | 0 | 0 | ✓ |
| Lees age 59: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 60: per-source sum == withdrawal | 0 | 0 | ✓ |
| Lees age 60: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 61: per-source sum == withdrawal | 0 | 0 | ✓ |
| Lees age 61: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 62: per-source sum == withdrawal | 48,939 | 48,939 | ✓ |
| Lees age 62: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 63: per-source sum == withdrawal | 52,567 | 52,567 | ✓ |
| Lees age 63: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 64: per-source sum == withdrawal | 149,180 | 149,180 | ✓ |
| Lees age 64: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 65: per-source sum == withdrawal | 149,883 | 149,883 | ✓ |
| Lees age 65: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 66: per-source sum == withdrawal | 153,990 | 153,989 | ✓ |
| Lees age 66: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 67: per-source sum == withdrawal | 144,976 | 144,976 | ✓ |
| Lees age 67: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 68: per-source sum == withdrawal | 149,493 | 149,493 | ✓ |
| Lees age 68: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 69: per-source sum == withdrawal | 120,869 | 120,869 | ✓ |
| Lees age 69: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 70: per-source sum == withdrawal | 39,604 | 39,604 | ✓ |
| Lees age 70: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 71: per-source sum == withdrawal | 41,864 | 41,864 | ✓ |
| Lees age 71: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 72: per-source sum == withdrawal | 44,219 | 44,219 | ✓ |
| Lees age 72: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 73: per-source sum == withdrawal | 46,671 | 46,671 | ✓ |
| Lees age 73: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 74: per-source sum == withdrawal | 49,224 | 49,224 | ✓ |
| Lees age 74: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 75: per-source sum == withdrawal | 51,884 | 51,884 | ✓ |
| Lees age 75: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 76: per-source sum == withdrawal | 54,653 | 54,653 | ✓ |
| Lees age 76: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 77: per-source sum == withdrawal | 57,535 | 57,535 | ✓ |
| Lees age 77: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 78: per-source sum == withdrawal | 60,535 | 60,535 | ✓ |
| Lees age 78: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 79: per-source sum == withdrawal | 63,659 | 63,659 | ✓ |
| Lees age 79: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 80: per-source sum == withdrawal | 66,909 | 66,909 | ✓ |
| Lees age 80: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 81: per-source sum == withdrawal | 70,291 | 70,291 | ✓ |
| Lees age 81: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 82: per-source sum == withdrawal | 73,810 | 73,810 | ✓ |
| Lees age 82: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 83: per-source sum == withdrawal | 77,472 | 77,472 | ✓ |
| Lees age 83: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 84: per-source sum == withdrawal | 81,282 | 81,282 | ✓ |
| Lees age 84: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 85: per-source sum == withdrawal | 85,245 | 85,245 | ✓ |
| Lees age 85: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 86: per-source sum == withdrawal | 89,366 | 89,366 | ✓ |
| Lees age 86: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 87: per-source sum == withdrawal | 93,654 | 93,654 | ✓ |
| Lees age 87: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 88: per-source sum == withdrawal | 98,114 | 98,114 | ✓ |
| Lees age 88: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 89: per-source sum == withdrawal | 102,751 | 102,751 | ✓ |
| Lees age 89: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 90: per-source sum == withdrawal | 107,573 | 107,573 | ✓ |
| Lees age 90: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 91: per-source sum == withdrawal | 112,588 | 112,588 | ✓ |
| Lees age 91: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 92: per-source sum == withdrawal | 117,803 | 117,803 | ✓ |
| Lees age 92: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 93: per-source sum == withdrawal | 123,223 | 123,223 | ✓ |
| Lees age 93: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 94: per-source sum == withdrawal | 128,859 | 128,859 | ✓ |
| Lees age 94: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Lees age 95: per-source sum == withdrawal | 134,718 | 134,718 | ✓ |
| Lees age 95: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 63: per-source sum == withdrawal | 0 | 0 | ✓ |
| Washingtons age 63: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 64: per-source sum == withdrawal | 0 | 0 | ✓ |
| Washingtons age 64: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 65: per-source sum == withdrawal | 19,095 | 19,095 | ✓ |
| Washingtons age 65: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 66: per-source sum == withdrawal | 22,199 | 22,199 | ✓ |
| Washingtons age 66: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 67: per-source sum == withdrawal | 90,544 | 90,544 | ✓ |
| Washingtons age 67: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 68: per-source sum == withdrawal | 95,005 | 95,005 | ✓ |
| Washingtons age 68: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 69: per-source sum == withdrawal | 74,876 | 74,876 | ✓ |
| Washingtons age 69: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 70: per-source sum == withdrawal | 28,527 | 28,527 | ✓ |
| Washingtons age 70: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 71: per-source sum == withdrawal | 30,010 | 30,010 | ✓ |
| Washingtons age 71: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 72: per-source sum == withdrawal | 31,555 | 31,555 | ✓ |
| Washingtons age 72: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 73: per-source sum == withdrawal | 21,438 | 21,438 | ✓ |
| Washingtons age 73: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 74: per-source sum == withdrawal | 11,210 | 11,210 | ✓ |
| Washingtons age 74: wdPretaxRMD == rmd - qcd | 0 | 0 | ✓ |
| Washingtons age 75: per-source sum == withdrawal | 107,168 | 107,168 | ✓ |
| Washingtons age 75: wdPretaxRMD == rmd - qcd | 90,408 | 90,408 | ✓ |
| Washingtons age 76: per-source sum == withdrawal | 115,732 | 115,732 | ✓ |
| Washingtons age 76: wdPretaxRMD == rmd - qcd | 96,329 | 96,329 | ✓ |
| Washingtons age 77: per-source sum == withdrawal | 124,283 | 124,283 | ✓ |
| Washingtons age 77: wdPretaxRMD == rmd - qcd | 102,172 | 102,172 | ✓ |
| Washingtons age 78: per-source sum == withdrawal | 133,941 | 133,941 | ✓ |
| Washingtons age 78: wdPretaxRMD == rmd - qcd | 108,827 | 108,827 | ✓ |
| Washingtons age 79: per-source sum == withdrawal | 144,213 | 144,213 | ✓ |
| Washingtons age 79: wdPretaxRMD == rmd - qcd | 115,893 | 115,893 | ✓ |
| Washingtons age 80: per-source sum == withdrawal | 155,237 | 155,237 | ✓ |
| Washingtons age 80: wdPretaxRMD == rmd - qcd | 123,391 | 123,391 | ✓ |
| Washingtons age 81: per-source sum == withdrawal | 166,379 | 166,379 | ✓ |
| Washingtons age 81: wdPretaxRMD == rmd - qcd | 130,667 | 130,667 | ✓ |
| Washingtons age 82: per-source sum == withdrawal | 494,293 | 494,293 | ✓ |
| Washingtons age 82: wdPretaxRMD == rmd - qcd | 139,059 | 139,059 | ✓ |
| Washingtons age 83: per-source sum == withdrawal | 521,964 | 521,964 | ✓ |
| Washingtons age 83: wdPretaxRMD == rmd - qcd | 147,111 | 147,111 | ✓ |
| Washingtons age 84: per-source sum == withdrawal | 559,472 | 559,472 | ✓ |
| Washingtons age 84: wdPretaxRMD == rmd - qcd | 156,472 | 156,472 | ✓ |
| Washingtons age 85: per-source sum == withdrawal | 432,517 | 432,517 | ✓ |
| Washingtons age 85: wdPretaxRMD == rmd - qcd | 165,332 | 165,332 | ✓ |
| Washingtons age 86: per-source sum == withdrawal | 170,118 | 170,118 | ✓ |
| Washingtons age 86: wdPretaxRMD == rmd - qcd | 170,118 | 170,118 | ✓ |
| Washingtons age 87: per-source sum == withdrawal | 266,664 | 266,664 | ✓ |
| Washingtons age 87: wdPretaxRMD == rmd - qcd | 179,498 | 179,498 | ✓ |
| Washingtons age 88: per-source sum == withdrawal | 271,567 | 271,567 | ✓ |
| Washingtons age 88: wdPretaxRMD == rmd - qcd | 187,857 | 187,857 | ✓ |
| Washingtons age 89: per-source sum == withdrawal | 287,183 | 287,183 | ✓ |
| Washingtons age 89: wdPretaxRMD == rmd - qcd | 197,891 | 197,891 | ✓ |
| Washingtons age 90: per-source sum == withdrawal | 301,093 | 301,093 | ✓ |
| Washingtons age 90: wdPretaxRMD == rmd - qcd | 206,536 | 206,536 | ✓ |
| Washingtons age 91: per-source sum == withdrawal | 315,201 | 315,201 | ✓ |
| Washingtons age 91: wdPretaxRMD == rmd - qcd | 215,229 | 215,229 | ✓ |
| Washingtons age 92: per-source sum == withdrawal | 329,413 | 329,413 | ✓ |
| Washingtons age 92: wdPretaxRMD == rmd - qcd | 223,898 | 223,898 | ✓ |
| Washingtons age 93: per-source sum == withdrawal | 343,579 | 343,579 | ✓ |
| Washingtons age 93: wdPretaxRMD == rmd - qcd | 232,455 | 232,455 | ✓ |
| Washingtons age 94: per-source sum == withdrawal | 354,318 | 354,318 | ✓ |
| Washingtons age 94: wdPretaxRMD == rmd - qcd | 238,254 | 238,254 | ✓ |
| Washingtons age 95: per-source sum == withdrawal | 364,456 | 364,456 | ✓ |
| Washingtons age 95: wdPretaxRMD == rmd - qcd | 243,474 | 243,474 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Already-retired plan: projection length matches age range | 29 | 29 | ✓ |
| Past startAge (62) for 67-year-old: engine does not throw | 0 | 0 | ✓ |
| Long conversion window (67-95, $1M pretax): non-zero total conversions | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| T1: primary transition, spouse on ACA, other source | 22,335 | 22,335 | ✓ |
| T2: primary transition, spouse on Medicare via SSDI | 3,191 | 3,191 | ✓ |
| T3: spouse transition, primary still pre-Medicare | 23,428 | 23,428 | ✓ |
| T4: spouse transition, primary on Medicare (Srini age 71) | 8,552 | 8,552 | ✓ |
| T5 (regression): single transition unchanged | 8,682 | 8,682 | ✓ |
| T6 (regression): both transition same year, other source | 14,358 | 14,358 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Test A: mixed over-routing — total = 55000 (no over-allocation) | 55,000 | 55,000 | ✓ |
| Test B: routed-routed clamp — savings conserved at 30000 | 30,000 | 30,000 | ✓ |
| Test B: routed-routed clamp — spouse pre-tax ≈ 12308 | 12,308 | 12,308 | ✓ |
| Test C: no-shortfall — savings === 70000 (savRatio=1 guard) | 70,000 | 70,000 | ✓ |
| Test C: no-shortfall — spouse pre-tax === 16000 (returns/balance guard) | 16,000 | 16,000 | ✓ |
| Test C: no-shortfall — brokerage === 31000 (returns/balance guard) | 31,000 | 31,000 | ✓ |
| Test D: no-limits block — savings conserved at 40000 | 40,000 | 40,000 | ✓ |
| Test D: no-limits block — spouse pre-tax ≈ 17273 | 17,273 | 17,273 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Percent mode: 10% of $100K income → $10,000 deferral (year 0) | 10,000 | 10,000 | ✓ |
| Percent mode tracks income: $10,000 deferral at year 0 (growth ×1) | 10,000 | 10,000 | ✓ |
| Percent mode tracks income: cumulative preTax $31,525 at year 2 | 31,525 | 31,525 | ✓ |
| Percent 10% of $100K ≡ dollar $10,000 — preTax identical every year | 7 | 7 | ✓ |
| Percent mode §402(g) clamp: 90% of $200K clamps identically to dollar $180K | 24,500 | 24,500 | ✓ |
| Percent mode §402(g) clamp: clamped deferral is below the raw $180,000 want | 1 | 1 | ✓ |
| Spouse percent mode: 10% of $60K spouse income ≡ dollar $6,000 (own income, not household) | 7 | 7 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Flag false is byte-identical to flag absent (migration safety) | 41 | 41 | ✓ |
| retireMonth < birthMonth shifts the retirement year +1 (born March, retire January) | 41 | 41 | ✓ |
| January birth produces no shift (retireMonth 0 < birthMonth 0 is false) | 41 | 41 | ✓ |
| retireMonth on or after birthMonth produces no shift (born March, retire June) | 41 | 41 | ✓ |
| Flag on: the age-65 row is still a working year (not retired) | 0 | 0 | ✓ |
| Flag on: the age-66 row is retired | 1 | 1 | ✓ |
| Flag off: the age-65 row is retired (bare age-counter, unchanged behavior) | 1 | 1 | ✓ |
| Partial-year proration rides the shifted year (equivalence) | 41 | 41 | ✓ |
| Shifted partial year: age-66 earned is 3/12 of the age-65 full year | 120,000 | 120,000 | ✓ |
| Spouse shift mirrors the primary path (spouse born July, retire January) | 41 | 41 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| TC1: single event at age 60 surfaces as oneTimeExpense=30000 | 30,000 | 30,000 | ✓ |
| TC1: another in-range row (age 70) has oneTimeExpense=0 | 0 | 0 | ✓ |
| TC2: two events at age 70 sum to oneTimeExpense=35000 | 35,000 | 35,000 | ✓ |
| TC3: empty oneTimeExpenses produces oneTimeExpense=0 on every row | 0 | 0 | ✓ |
| TC4: one-time expense does not leak into y.expenses in the spend year | 92,742 | 92,742 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| ACA cap binds — cliffFill converts strictly less than smartFill | 1 | 1 | ✓ |
| ACA cap binds — cliffFill still converts > 0 | 1 | 1 | ✓ |
| ACA cap inert post-Medicare — equals smartFill | 1 | 1 | ✓ |
| ACA cap inert under enhanced rules — equals smartFill | 1 | 1 | ✓ |
| ACA cap inert with non-ACA coverage — equals smartFill | 1 | 1 | ✓ |
| NIIT cap binds — cliffFill converts strictly less than smartFill | 1 | 1 | ✓ |
| NIIT off — equals smartFill | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Non-ACA baseline: total acaSubsidy = 0 (baseACA = 0) | 0 | 0 | ✓ |
| Non-ACA + conversions: candidate total acaSubsidy = 0 | 0 | 0 | ✓ |
| Non-ACA + cliffFill: total acaSubsidy = 0 | 0 | 0 | ✓ |
| Non-ACA cliffFill ties smartFill on all score-relevant totals | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| SS portion = wages × 6.2% | 6,200 | 6,200 | ✓ |
| Medicare portion = wages × 1.45% | 1,450 | 1,450 | ✓ |
| Total = SS + Medicare, no Addl Med here | 7,650 | 7,650 | ✓ |
| SS portion at cap = cap × 6.2% | 11,439 | 11,439 | ✓ |
| Medicare at cap = cap × 1.45% (no cap on Medicare) | 2,675 | 2,675 | ✓ |
| SS portion caps at wage base × 6.2% | 11,439 | 11,439 | ✓ |
| Medicare continues on full wage amount | 3,625 | 3,625 | ✓ |
| Zero wages → all zero | 0 | 0 | ✓ |
| SS portion limited to remaining base | 5,239 | 5,239 | ✓ |
| Medicare still on full wages | 2,175 | 2,175 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Having a pre-tax account reduces pre-retirement federal tax | 1 | 1 | ✓ |
| FICA unchanged by pre-tax 401(k) choice | 9,180 | 9,180 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| No phases → base income at year 0 | 120,000 | 120,000 | ✓ |
| Phase income overrides base during covered years | 150,000 | 150,000 | ✓ |
| Age outside phases uses base income | 120,000 | 120,000 | ✓ |
| Phase-level incomeBreakdown reduces FICA base by §125 + payrollHSA | 10,098 | 10,098 | ✓ |
| Phase SE does not pollute FICA (separate channels) | 0 | 0 | ✓ |
| W-2 + SE in same phase: FICA on W-2 only (unchanged by SE) | 7,650 | 7,650 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| $50K net SE: secaBase = $46,175 | 46,175 | 46,175 | ✓ |
| $50K net SE: SS portion = $5,726 | 5,726 | 5,726 | ✓ |
| $50K net SE: Medicare = $1,339 | 1,339 | 1,339 | ✓ |
| $50K net SE: total = $7,065 | 7,065 | 7,065 | ✓ |
| $50K net SE: halfDeduction ≈ $3,533 | 3,533 | 3,533 | ✓ |
| Negative SE: zero SE tax | 0 | 0 | ✓ |
| Negative SE: zero half-deduction | 0 | 0 | ✓ |
| $400 net SE: under SECA floor → zero SE tax | 0 | 0 | ✓ |
| Wage base sharing: SS portion limited | 4,278 | 4,278 | ✓ |
| Wage base sharing: Medicare uncapped | 2,143 | 2,143 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| SE tax in year output ≈ $11,304 | 11,304 | 11,304 | ✓ |
| seNetEarnings = $80K | 80,000 | 80,000 | ✓ |
| seHalfDeduction ≈ $5,652 | 5,652 | 5,652 | ✓ |
| SE loss: zero SE tax | 0 | 0 | ✓ |
| SE loss: seNetEarnings = -$20K | -20,000 | -20,000 | ✓ |
| SE loss: seHalfDeduction = 0 | 0 | 0 | ✓ |
| SE loss reduces MAGI vs baseline | 1 | 1 | ✓ |
| W-2 + SE: SE tax respects wage base sharing + Addl Medicare | 6,636 | 6,636 | ✓ |
| Working-age SE: seTax > 0 | 1 | 1 | ✓ |
| Spouse SE: seTax > 0 | 1 | 1 | ✓ |
| F-044: $220K SE single → seTax includes ~$29 Addl Medicare | 28,799 | 28,799 | ✓ |
| F-044: $130K W-2 + $145K SE → SE Addl Medicare via §1401(b)(2)(B) | 11,216 | 11,216 | ✓ |
| F-044: MFJ combined SE ($80K + $200K) → Addl Medicare on $8,580 excess | 39,615 | 39,615 | ✓ |
| F-044: $300K SE single → seTax includes $693 Addl Medicare | 31,605 | 31,605 | ✓ |
| F-044: §164(f) half-deduction is NOT inflated by Addl Medicare | 15,456 | 15,456 | ✓ |
| F-156: retired primary + spouse SE $300K → seTax includes $243 Addl Medicare | 31,155 | 31,155 | ✓ |
| F-156: retired primary + spouse SE $200K (below threshold) → no Addl Medicare | 28,234 | 28,234 | ✓ |
| F-156: retired primary + spouse W-2 $300K → fica reflects W-2 Addl Medicare ($450) | 16,239 | 16,239 | ✓ |
| F-156: retired primary + spouse W-2 $300K → seTax = 0 (no SE) | 0 | 0 | ✓ |
| F-156: working primary $300K SE → seTax = $31,605 (matches 23d, no double-count) | 31,605 | 31,605 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Below threshold MFJ: full 20% deduction | 20,000 | 20,000 | ✓ |
| SSTB above upper MFJ: $0 deduction | 0 | 0 | ✓ |
| Non-SSTB above upper MFJ no W-2: $400 floor | 400 | 400 | ✓ |
| SSTB phase-in midpoint MFJ: W-2=0 → $400 floor | 400 | 400 | ✓ |
| Taxable income limit MFJ: capped at 20% × (taxable − net capgain) | 10,000 | 10,000 | ✓ |
| QBI below $1,000: no deduction | 0 | 0 | ✓ |
| Single below threshold: full 20% deduction | 10,000 | 10,000 | ✓ |
| Single SSTB above upper: $0 deduction | 0 | 0 | ✓ |
| Two-business household MFJ: combined deduction | 16,000 | 16,000 | ✓ |
| Mixed SSTB/non-SSTB above upper: $400 floor via non-SSTB | 400 | 400 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| W-2 + SE: qbiDeduction > 0 | 1 | 1 | ✓ |
| W-2 + SE $100K: qbiDeduction ≈ 20% × (netSE − halfSE) | 18,932 | 18,932 | ✓ |
| Pure SE $100K: deduction capped by household taxable income limit | 1 | 1 | ✓ |
| SSTB in phase-in range: deduction reduced vs full | 1 | 1 | ✓ |
| SSTB conversion: conversionTax > 0 | 1 | 1 | ✓ |
| TLH + QBI: deduction computed (no asymmetry crash) | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| $100K netSE age 45 auto-max: employee = $24,500 | 24,500 | 24,500 | ✓ |
| $100K netSE age 45 auto-max: employer ≈ $18,587 | 18,587 | 18,587 | ✓ |
| Traditional employee classification | 24,500 | 24,500 | ✓ |
| Roth employee classification | 0 | 0 | ✓ |
| Age 62 SECURE 2.0 catch-up: employee = $35,750 | 35,750 | 35,750 | ✓ |
| §415(c) binds: employer + employee = annual addition limit | 72,000 | 72,000 | ✓ |
| §415(c) clamp produces employer = $47,500 | 47,500 | 47,500 | ✓ |
| §402(g) aggregation: Solo limited by W-2 deferral target | 4,500 | 4,500 | ✓ |
| Auto employer respects §415(c) | 18,587 | 18,587 | ✓ |
| Roth employee: tradEmployee = 0 | 0 | 0 | ✓ |
| Roth employee: rothEmployee tracks amount | 24,500 | 24,500 | ✓ |
| Roth employer: rothEmployerAmt > 0 | 18,587 | 18,587 | ✓ |
| Roth employer: tradEmployer = 0 | 0 | 0 | ✓ |
| Manual deferral $10K respected | 10,000 | 10,000 | ✓ |
| Manual employer 10% = $9,294 | 9,294 | 9,294 | ✓ |
| Negative SE: zero employee deferral | 0 | 0 | ✓ |
| Negative SE: zero employer profit-share | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| solo401kEmployee surfaces in year output | 24,500 | 24,500 | ✓ |
| solo401kEmployer ≈ $18,587 | 18,587 | 18,587 | ✓ |
| Traditional Solo reduces MAGI | 1 | 1 | ✓ |
| Roth employee preserves AGI | 1 | 1 | ✓ |
| Traditional Solo: qbiDeduction not larger than without Solo | 1 | 1 | ✓ |
| Roth employee preserves QBI deduction | 1 | 1 | ✓ |
| Roth employer: same QBI input as traditional employer (W-2+SE) | 1 | 1 | ✓ |
| Roth employer: higher MAGI than traditional employer | 1 | 1 | ✓ |
| Spouse Solo 401(k): contributions > 0 | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Pool >> target (regression): solo401kEmployee = 0 | 0 | 0 | ✓ |
| Pool < target, traditional Solo: solo401kEmployee = $9,500 (was $0 pre-fix) | 9,500 | 9,500 | ✓ |
| Pool < target, Roth Solo: solo401kEmployee = $9,500 (Roth deferrals count vs §402(g) per §402A) | 9,500 | 9,500 | ✓ |
| Percent W-2 (25% of $98K = $24,500) consumes §402(g): solo401kEmployee = 0 | 0 | 0 | ✓ |
| Percent W-2 + pool clamp frees §402(g) room: solo401kEmployee = $9,500 | 9,500 | 9,500 | ✓ |
| Spouse percent W-2 (E4): spouse Solo §402(g) capacity consumed, solo401kEmployee = 0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| SE loss year-0: qbiLossCarryforward (carry-in) = 0 | 0 | 0 | ✓ |
| SE loss year-0: qbiLossCarryforwardNext = $30,000 (loss magnitude) | 30,000 | 30,000 | ✓ |
| Loss y0 → profit y1: y1 qbiLossCarryforward = $30,000 (carry-in from y0) | 30,000 | 30,000 | ✓ |
| Loss y0 → profit y1: y1 qbiLossCarryforwardNext = 0 (carry absorbed) | 0 | 0 | ✓ |
| Pre-existing carry $25K + y0 profit $40K: y0 qbiLossCarryforward = $25K (carry-in from params) | 25,000 | 25,000 | ✓ |
| Pre-existing carry $25K + y0 profit $40K: y0 qbiLossCarryforwardNext = 0 (absorbed) | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| S-corp $100K salary: employer auto 25% = $25,000 | 25,000 | 25,000 | ✓ |
| S-corp $100K salary: employee auto-max = $24,500 | 24,500 | 24,500 | ✓ |
| S-corp: §415(c) not binding ($49,500 < $72,000) | 49,500 | 49,500 | ✓ |
| S-corp manual 15%: employer = $15,000 | 15,000 | 15,000 | ✓ |
| S-corp $300K salary: §415(c) binds | 72,000 | 72,000 | ✓ |
| S-corp §415(c): employer clamped to $47,500 | 47,500 | 47,500 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| S-corp: seTax = 0 (IRC §1402(a)(13)) | 0 | 0 | ✓ |
| S-corp: FICA on salary ≈ $7,650 | 7,650 | 7,650 | ✓ |
| S-corp QBI deduction ≈ $10,100 | 10,100 | 10,100 | ✓ |
| S-corp age 62: QBI deduction ≈ $7,850 | 7,850 | 7,850 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| §603: trad base = $24,500 | 24,500 | 24,500 | ✓ |
| §603: forced Roth catch-up = $8,000 | 8,000 | 8,000 | ✓ |
| §603: total employee unchanged = $32,500 | 32,500 | 32,500 | ✓ |
| §603 + §109: trad base = $24,500 | 24,500 | 24,500 | ✓ |
| §603 + §109: forced Roth super catch-up = $11,250 | 11,250 | 11,250 | ✓ |
| §603 below threshold: all traditional = $32,500 | 32,500 | 32,500 | ✓ |
| §603 below threshold: no forced Roth | 0 | 0 | ✓ |
| §603 sole prop: all traditional = $32,500 | 32,500 | 32,500 | ✓ |
| §603 sole prop: no forced Roth | 0 | 0 | ✓ |
| §603 + already Roth: tradEmployee = 0 | 0 | 0 | ✓ |
| §603 + already Roth: all Roth = $32,500 | 32,500 | 32,500 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| S-corp above upper: W-2 limit not binding ($10,100) | 10,100 | 10,100 | ✓ |
| Above upper, low W-2: limit binds at $10,000 | 10,000 | 10,000 | ✓ |
| UBIA path wins: 25%W2+2.5%UBIA = $17,500 | 17,500 | 17,500 | ✓ |
| Phase-in non-SSTB: W-2 high, no reduction = $16,000 | 16,000 | 16,000 | ✓ |
| Phase-in non-SSTB low W-2: reduction ≈ $13,917 | 13,917 | 13,917 | ✓ |
| Phase-in SSTB: phased amounts ≈ $5,707 | 5,707 | 5,707 | ✓ |
| Zero W-2 above upper: $400 floor (backward compat) | 400 | 400 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| SEP sole prop: employer 20% of planComp = $18,587 | 18,587 | 18,587 | ✓ |
| SEP sole prop: employee = $0 (no deferral) | 0 | 0 | ✓ |
| SEP S-corp: employer 25% of salary = $25,000 | 25,000 | 25,000 | ✓ |
| SEP S-corp: employee = $0 | 0 | 0 | ✓ |
| SEP S-corp: QBI deduction ≈ $15,000 | 15,000 | 15,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| $100K netSE age 45: employee = $17,000 | 17,000 | 17,000 | ✓ |
| $100K netSE age 45: employer match = $3,000 | 3,000 | 3,000 | ✓ |
| Traditional employee classification | 17,000 | 17,000 | ✓ |
| Age 52 catch-up: employee = $21,000 | 21,000 | 21,000 | ✓ |
| Age 52: employer match still = $3,000 | 3,000 | 3,000 | ✓ |
| Age 62 SECURE 2.0 super catch-up: employee = $22,250 | 22,250 | 22,250 | ✓ |
| Non-elective: employer = $2,000 | 2,000 | 2,000 | ✓ |
| Non-elective: employee still auto-maxes = $17,000 | 17,000 | 17,000 | ✓ |
| Manual $10K deferral respected | 10,000 | 10,000 | ✓ |
| Match mirrors deferral (< 3% cap): $3,000 | 3,000 | 3,000 | ✓ |
| S-corp: employer match based on W-2 salary = $2,400 | 2,400 | 2,400 | ✓ |
| S-corp: employee deferral = $17,000 | 17,000 | 17,000 | ✓ |
| §402(g) aggregation: deferral capped at $4,500 | 4,500 | 4,500 | ✓ |
| Match tracks actual deferral: min($3K, $4.5K) = $3,000 | 3,000 | 3,000 | ✓ |
| Negative SE: zero employee deferral | 0 | 0 | ✓ |
| Negative SE: zero employer contribution | 0 | 0 | ✓ |
| §603: tradEmployee = $17,000 (base) | 17,000 | 17,000 | ✓ |
| §603: rothEmployee = $4,000 (catch-up forced Roth) | 4,000 | 4,000 | ✓ |
| §603: total deferral = $21,000 | 21,000 | 21,000 | ✓ |
| Roth employee: tradEmployee = 0 | 0 | 0 | ✓ |
| Roth employee: rothEmployee = $17,000 | 17,000 | 17,000 | ✓ |
| Employer always traditional: tradEmployer = $3,000 | 3,000 | 3,000 | ✓ |
| Employer rothEmployerAmt = 0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| solo401kEmployee = SIMPLE deferral ≈ $17,000 | 17,000 | 17,000 | ✓ |
| solo401kEmployer = match ≈ $3,000 | 3,000 | 3,000 | ✓ |
| Traditional SIMPLE reduces AGI | 1 | 1 | ✓ |
| Non-elective employer ≈ $2,000 | 2,000 | 2,000 | ✓ |
| Traditional SIMPLE reduces QBI deduction | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Auto-max: afterTax = $28,913 | 28,913 | 28,913 | ✓ |
| Employee + employer unchanged | 43,087 | 43,087 | ✓ |
| Manual cap $15K respected | 15,000 | 15,000 | ✓ |
| §415(c) ceiling: afterTax = $0 | 0 | 0 | ✓ |
| Disabled: afterTax = 0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| solo401kAfterTax > 0 in year output | 1 | 1 | ✓ |
| solo401kAfterTax ≈ $28,913 | 28,913 | 28,913 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| calcSpousalBenefit exists | function | function | ✓ |
| calcSpousalBenefit has 4 parameters (not old 2-param version) | 4 | 4 | ✓ |
| ssReverseAdjustPIA exists | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Spouse at FRA (67), no own record → 50% of worker PIA | 12,000 | 12,000 | ✓ |
| Worker PIA = $3,000/mo, spouse at FRA → $18,000/yr floor | 18,000 | 18,000 | ✓ |
| Spouse own = $9,600/yr (< $12K floor) → top-up = $2,400 | 2,400 | 2,400 | ✓ |
| Spouse own = $600/mo ($7,200/yr) → top-up = $4,800 | 4,800 | 4,800 | ✓ |
| Spouse own = $12,000/yr (= floor) → top-up = $0 | 0 | 0 | ✓ |
| Spouse own = $13,200/yr (> floor) → top-up = $0 | 0 | 0 | ✓ |
| Spouse claims at 62 (60mo early), no own record → 32.5% of worker PIA | 7,800 | 7,800 | ✓ |
| Spouse claims at 65 (24mo early) → ~41.7% of worker PIA annual | 10,000 | 10,000 | ✓ |
| FRA=66, claims at 62 (48mo early) → 35% of worker PIA | 8,400 | 8,400 | ✓ |
| Spouse claims at 70 (past FRA) → same as FRA, no DRC increase | 12,000 | 12,000 | ✓ |
| Spouse claims at 68 (past FRA) → same as FRA | 12,000 | 12,000 | ✓ |
| Worker PIA = $0 → top-up = $0 | 0 | 0 | ✓ |
| Spouse own = $0, worker PIA = $0 → $0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Spousal benefit grows with COLA at age 68 vs 67 | 1 | 1 | ✓ |
| Spousal top-up at 68 ≈ $18,360 (18,000 × 1.02) | 18,360 | 18,360 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| "Spousal SS Benefit" Plan Health lever title exists in HTML | 1 | 1 | ✓ |
| Lever says benefit is "already included in your projection" | 1 | 1 | ✓ |
| Lever links to SS tab (inputTab=ss) | 1 | 1 | ✓ |
| Old 2-param calcSpousalBenefit signature is NOT present | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Primary dies y0, survivor past FRA → survivor benefit applies immediately, not deferred to age 85 | 58,344 | 58,344 | ✓ |
| Primary dies y0, survivor benefit holds across years (not just y0) | 58,344 | 58,344 | ✓ |
| Spouse dies y0, primary is lower earner → survivor benefit dominates immediately | 60,000 | 60,000 | ✓ |
| Pre-FRA strategic claim age respected — survivor 62 with claim age 65 → no benefit yet | 1 | 1 | ✓ |
| Pre-FRA strategic claim age respected — survivor benefit starts at chosen age 65 | 1 | 1 | ✓ |
| User-set claim age above FRA is capped — no waiting past FRA per RS 00615.301 | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| PIA $1500 claim 62 → adj $1050 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $1500 claim 65 → adj $1300 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $1500 claim 67 → adj $1500 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $1500 claim 68 → adj $1620 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $1500 claim 70 → adj $1860 → reverse $1500 | 1,500 | 1,500 | ✓ |
| PIA $2500 claim 62 → adj $1750 → reverse $2500 | 2,500 | 2,500 | ✓ |
| PIA $2500 claim 65 → adj $2166 → reverse $2499 | 2,500 | 2,499 | ✓ |
| PIA $2500 claim 67 → adj $2500 → reverse $2500 | 2,500 | 2,500 | ✓ |
| PIA $2500 claim 68 → adj $2700 → reverse $2500 | 2,500 | 2,500 | ✓ |
| PIA $2500 claim 70 → adj $3100 → reverse $2500 | 2,500 | 2,500 | ✓ |
| PIA $3500 claim 62 → adj $2450 → reverse $3500 | 3,500 | 3,500 | ✓ |
| PIA $3500 claim 65 → adj $3033 → reverse $3500 | 3,500 | 3,500 | ✓ |
| PIA $3500 claim 67 → adj $3500 → reverse $3500 | 3,500 | 3,500 | ✓ |
| PIA $3500 claim 68 → adj $3780 → reverse $3500 | 3,500 | 3,500 | ✓ |
| PIA $3500 claim 70 → adj $4340 → reverse $3500 | 3,500 | 3,500 | ✓ |
| Zero current monthly returns 0 | 0 | 0 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Age 75: ss = current monthly × 12 (COLA 0%) | 50,400 | 50,400 | ✓ |
| Age 80: ss still 50400 (COLA 0%) | 50,400 | 50,400 | ✓ |
| Age 75 with COLA 2%: ss = 50400 (zero COLA years) | 50,400 | 50,400 | ✓ |
| Age 80 with COLA 2%: ss ≈ 50400 × 1.02^5 ≈ 55648 | 55,648 | 55,646 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Already-collecting primary supplies recovered PIA for spousal floor | 54,000 | 54,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Primary not yet filed, spouse already collecting → ss > 0 | 1 | 1 | ✓ |
| Spouse benefit constant when primary still not filed (COLA 0%) | 1 | 1 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Year 0 (age 73): combined ss ≈ (3500+1800) × 12 = 63600 | 63,600 | 63,600 | ✓ |
| Five years later (age 78): combined ss ≈ 63600 × 1.02^5 ≈ 70221 | 70,221 | 70,219 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Non-working spouse with ssAlreadyCollecting:false → $54K at FRA | 54,000 | 54,000 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2026 | 2,026 | 2,026 | ✓ |
| age = 64 | 64 | 64 | ✓ |
| spAge = 62 | 62 | 62 | ✓ |
| earned = 182000 | 182,000 | 182,000 | ✓ |
| primaryEarned = 110000 | 110,000 | 110,000 | ✓ |
| spouseEarned = 72000 | 72,000 | 72,000 | ✓ |
| savings = 28000 | 28,000 | 28,000 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 0 | 0 | 0 | ✓ |
| ssTaxable = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 0 | 0 | 0 | ✓ |
| capGains = 0 | 0 | 0 | ✓ |
| agi = 165200 | 165,200 | 165,200 | ✓ |
| fedTax = 17202 | 17,202 | 17,202 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 13923 | 13,923 | 13,923 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 31125 | 31,125 | 31,125 | ✓ |
| medicareCost = 1826 | 1,826 | 1,826 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| preTax = 873976 | 873,976 | 873,976 | ✓ |
| roth = 167990 | 167,990 | 167,990 | ✓ |
| taxable = 111494 | 111,494 | 111,494 | ✓ |
| netWorth = 1153460 | 1,153,460 | 1,153,460 | ✓ |
| retired === null | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2027 | 2,027 | 2,027 | ✓ |
| age = 65 | 65 | 65 | ✓ |
| spAge = 63 | 63 | 63 | ✓ |
| retired = true | true | true | ✓ |
| earned = 73440 | 73,440 | 73,440 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 73440 | 73,440 | 73,440 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 0 | 0 | 0 | ✓ |
| ssTaxable = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 19807 | 19,807 | 19,807 | ✓ |
| capGains = 9904 | 9,904 | 9,904 | ✓ |
| agi = 77224 | 77,224 | 77,224 | ✓ |
| fedTax = 2672 | 2,672 | 2,672 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 5618 | 5,618 | 5,618 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 8290 | 8,290 | 8,290 | ✓ |
| medicareCost = 2557 | 2,557 | 2,557 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| preTax = 935154 | 935,154 | 935,154 | ✓ |
| roth = 179749 | 179,749 | 179,749 | ✓ |
| taxable = 98105 | 98,105 | 98,105 | ✓ |
| netWorth = 1213008 | 1,213,008 | 1,213,008 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2029 | 2,029 | 2,029 | ✓ |
| age = 67 | 67 | 67 | ✓ |
| spAge = 65 | 65 | 65 | ✓ |
| retired = true | true | true | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 33110 | 33,110 | 33,110 | ✓ |
| ssTaxable = 8149 | 8,149 | 8,149 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 59946 | 59,946 | 59,946 | ✓ |
| capGains = 29973 | 29,973 | 29,973 | ✓ |
| agi = 38122 | 38,122 | 38,122 | ✓ |
| fedTax = 0 | 0 | 0 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 0 | 0 | 0 | ✓ |
| medicareCost = 5638 | 5,638 | 5,638 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| preTax = 1070658 | 1,070,658 | 1,070,658 | ✓ |
| roth = 205795 | 205,795 | 205,795 | ✓ |
| taxable = 23936 | 23,936 | 23,936 | ✓ |
| netWorth = 1300389 | 1,300,389 | 1,300,389 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2031 | 2,031 | 2,031 | ✓ |
| age = 69 | 69 | 69 | ✓ |
| spAge = 67 | 67 | 67 | ✓ |
| retired = true | true | true | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 59620 | 59,620 | 59,620 | ✓ |
| ssTaxable = 30096 | 30,096 | 30,096 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 42538 | 42,538 | 42,538 | ✓ |
| capGains = 0 | 0 | 0 | ✓ |
| agi = 72634 | 72,634 | 72,634 | ✓ |
| fedTax = 3210 | 3,210 | 3,210 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 3210 | 3,210 | 3,210 | ✓ |
| medicareCost = 6215 | 6,215 | 6,215 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| preTax = 1133076 | 1,133,076 | 1,133,076 | ✓ |
| roth = 235615 | 235,615 | 235,615 | ✓ |
| taxable = 0 | 0 | 0 | ✓ |
| netWorth = 1368691 | 1,368,691 | 1,368,691 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2037 | 2,037 | 2,037 | ✓ |
| age = 75 | 75 | 75 | ✓ |
| spAge = 73 | 73 | 73 | ✓ |
| retired = true | true | true | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 67142 | 67,142 | 67,142 | ✓ |
| ssTaxable = 46306 | 46,306 | 46,306 | ✓ |
| rmd = 52218 | 52,218 | 52,218 | ✓ |
| rmdRequired = true | true | true | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 57848 | 57,848 | 57,848 | ✓ |
| capGains = 0 | 0 | 0 | ✓ |
| agi = 104154 | 104,154 | 104,154 | ✓ |
| fedTax = 5926 | 5,926 | 5,926 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 5926 | 5,926 | 5,926 | ✓ |
| medicareCost = 8329 | 8,329 | 8,329 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| preTax = 1312596 | 1,312,596 | 1,312,596 | ✓ |
| roth = 353594 | 353,594 | 353,594 | ✓ |
| taxable = 0 | 0 | 0 | ✓ |
| netWorth = 1666190 | 1,666,190 | 1,666,190 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2057 | 2,057 | 2,057 | ✓ |
| age = 95 | 95 | 95 | ✓ |
| spAge = 93 | 93 | 93 | ✓ |
| retired = true | true | true | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 99770 | 99,770 | 99,770 | ✓ |
| ssTaxable = 84805 | 84,805 | 84,805 | ✓ |
| rmd = 140446 | 140,446 | 140,446 | ✓ |
| rmdRequired = true | true | true | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 213238 | 213,238 | 213,238 | ✓ |
| capGains = 36396 | 36,396 | 36,396 | ✓ |
| agi = 261646 | 261,646 | 261,646 | ✓ |
| fedTax = 15589 | 15,589 | 15,589 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 443 | 443 | 443 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 15589 | 15,589 | 15,589 | ✓ |
| medicareCost = 22100 | 22,100 | 22,100 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| preTax = 1187188 | 1,187,188 | 1,187,188 | ✓ |
| roth = 1368298 | 1,368,298 | 1,368,298 | ✓ |
| taxable = 80584 | 80,584 | 80,584 | ✓ |
| netWorth = 2636070 | 2,636,070 | 2,636,070 | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2026 | 2,026 | 2,026 | ✓ |
| age = 59 | 59 | 59 | ✓ |
| spAge = 57 | 57 | 57 | ✓ |
| earned = 267000 | 267,000 | 267,000 | ✓ |
| primaryEarned = 175000 | 175,000 | 175,000 | ✓ |
| spouseEarned = 92000 | 92,000 | 92,000 | ✓ |
| totalInc = 267000 | 267,000 | 267,000 | ✓ |
| savings = 43000 | 43,000 | 43,000 | ✓ |
| hsaContrib = 8550 | 8,550 | 8,550 | ✓ |
| cashInterest = 2250 | 2,250 | 2,250 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 0 | 0 | 0 | ✓ |
| ssTaxable = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 0 | 0 | 0 | ✓ |
| capGains = 0 | 0 | 0 | ✓ |
| agi = 245700 | 245,700 | 245,700 | ✓ |
| fedTax = 36436 | 36,436 | 36,436 | ✓ |
| stateTax = 13870 | 13,870 | 13,870 | ✓ |
| fica = 19848 | 19,848 | 19,848 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 70154 | 70,154 | 70,154 | ✓ |
| medicareCost = 0 | 0 | 0 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| acaSubsidy = 0 | 0 | 0 | ✓ |
| expenses = 88000 | 88,000 | 88,000 | ✓ |
| preTax = 1193050 | 1,193,050 | 1,193,050 | ✓ |
| roth = 217745 | 217,745 | 217,745 | ✓ |
| taxable = 324565 | 324,565 | 324,565 | ✓ |
| hsa = 46163 | 46,163 | 46,163 | ✓ |
| netWorth = 1781523 | 1,781,523 | 1,781,523 | ✓ |
| retired === null | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2029 | 2,029 | 2,029 | ✓ |
| age = 62 | 62 | 62 | ✓ |
| spAge = 60 | 60 | 60 | ✓ |
| earned = 97631 | 97,631 | 97,631 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 97631 | 97,631 | 97,631 | ✓ |
| totalInc = 97631 | 97,631 | 97,631 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| hsaContrib = 8550 | 8,550 | 8,550 | ✓ |
| cashInterest = 365 | 365 | 365 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 0 | 0 | 0 | ✓ |
| ssTaxable = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| rothConv = 163554 | 163,554 | 163,554 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 48939 | 48,939 | 48,939 | ✓ |
| withdrawalForSpending = 48939 | 48,939 | 48,939 | ✓ |
| capGains = 0 | 0 | 0 | ✓ |
| agi = 238000 | 238,000 | 238,000 | ✓ |
| fedTax = 33076 | 33,076 | 33,076 | ✓ |
| stateTax = 13428 | 13,428 | 13,428 | ✓ |
| fica = 7469 | 7,469 | 7,469 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 28901 | 28,901 | 28,901 | ✓ |
| tax = 53973 | 53,973 | 53,973 | ✓ |
| drawdownTax = 0 | 0 | 0 | ✓ |
| taxAwareDrawdown = 0 | 0 | 0 | ✓ |
| medicareCost = 0 | 0 | 0 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| healthcare = 25338 | 25,338 | 25,338 | ✓ |
| acaSubsidy = 0 | 0 | 0 | ✓ |
| acaBenchmark = 25338 | 25,338 | 25,338 | ✓ |
| acaNetPremium = 25338 | 25,338 | 25,338 | ✓ |
| acaFPLPercent = 1144 | 1,144 | 1,144 | ✓ |
| expenses = 121498 | 121,498 | 121,498 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| preTax = 1322084 | 1,322,084 | 1,322,084 | ✓ |
| preTaxPrimary = 949207 | 949,207 | 949,207 | ✓ |
| preTaxSpouse = 372877 | 372,877 | 372,877 | ✓ |
| roth = 497443 | 497,443 | 497,443 | ✓ |
| taxable = 325069 | 325,069 | 325,069 | ✓ |
| taxableInv = 325069 | 325,069 | 325,069 | ✓ |
| hsa = 83834 | 83,834 | 83,834 | ✓ |
| netWorth = 2228430 | 2,228,430 | 2,228,430 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === "aca" | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2031 | 2,031 | 2,031 | ✓ |
| age = 64 | 64 | 64 | ✓ |
| spAge = 62 | 62 | 62 | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| totalInc = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| cashInterest = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 0 | 0 | 0 | ✓ |
| ssTaxable = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| rothConv = 199513 | 199,513 | 199,513 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 149180 | 149,180 | 149,180 | ✓ |
| withdrawalForSpending = 149180 | 149,180 | 149,180 | ✓ |
| capGains = 53487 | 53,487 | 53,487 | ✓ |
| agi = 253000 | 253,000 | 253,000 | ✓ |
| fedTax = 31147 | 31,147 | 31,147 | ✓ |
| stateTax = 14290 | 14,290 | 14,290 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| cgTax = 8023 | 8,023 | 8,023 | ✓ |
| niitTax = 114 | 114 | 114 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 23010 | 23,010 | 23,010 | ✓ |
| tax = 45437 | 45,437 | 45,437 | ✓ |
| drawdownTax = 0 | 0 | 0 | ✓ |
| taxAwareDrawdown = 0 | 0 | 0 | ✓ |
| medicareCost = 1295 | 1,295 | 1,295 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| healthcare = 23441 | 23,441 | 23,441 | ✓ |
| acaSubsidy = 0 | 0 | 0 | ✓ |
| acaBenchmark = 23441 | 23,441 | 23,441 | ✓ |
| acaNetPremium = 23441 | 23,441 | 23,441 | ✓ |
| acaFPLPercent = 1049 | 1,049 | 1,049 | ✓ |
| expenses = 126752 | 126,752 | 126,752 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| preTax = 1106726 | 1,106,726 | 1,106,726 | ✓ |
| preTaxPrimary = 794588 | 794,588 | 794,588 | ✓ |
| preTaxSpouse = 312138 | 312,138 | 312,138 | ✓ |
| roth = 976451 | 976,451 | 976,451 | ✓ |
| taxable = 198957 | 198,957 | 198,957 | ✓ |
| taxableInv = 198957 | 198,957 | 198,957 | ✓ |
| hsa = 0 | 0 | 0 | ✓ |
| netWorth = 2282134 | 2,282,134 | 2,282,134 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === "aca" | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2037 | 2,037 | 2,037 | ✓ |
| age = 70 | 70 | 70 | ✓ |
| spAge = 68 | 68 | 68 | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| totalInc = 90538 | 90,538 | 90,538 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| cashInterest = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 90538 | 90,538 | 90,538 | ✓ |
| ssTaxable = 18556 | 18,556 | 18,556 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 39604 | 39,604 | 39,604 | ✓ |
| withdrawalForSpending = 39604 | 39,604 | 39,604 | ✓ |
| capGains = 0 | 0 | 0 | ✓ |
| agi = 32059 | 32,059 | 32,059 | ✓ |
| fedTax = 0 | 0 | 0 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 0 | 0 | 0 | ✓ |
| drawdownTax = 0 | 0 | 0 | ✓ |
| taxAwareDrawdown = 0 | 0 | 0 | ✓ |
| medicareCost = 8329 | 8,329 | 8,329 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| healthcare = 0 | 0 | 0 | ✓ |
| acaSubsidy = 0 | 0 | 0 | ✓ |
| acaBenchmark = 0 | 0 | 0 | ✓ |
| acaNetPremium = 0 | 0 | 0 | ✓ |
| acaFPLPercent = 0 | 0 | 0 | ✓ |
| expenses = 130142 | 130,142 | 130,142 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| preTax = 0 | 0 | 0 | ✓ |
| preTaxPrimary = 0 | 0 | 0 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 2401201 | 2,401,201 | 2,401,201 | ✓ |
| taxable = 0 | 0 | 0 | ✓ |
| taxableInv = 0 | 0 | 0 | ✓ |
| hsa = 0 | 0 | 0 | ✓ |
| netWorth = 2401201 | 2,401,201 | 2,401,201 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === false | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2042 | 2,042 | 2,042 | ✓ |
| age = 75 | 75 | 75 | ✓ |
| spAge = 73 | 73 | 73 | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| totalInc = 99961 | 99,961 | 99,961 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| cashInterest = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 99962 | 99,962 | 99,961 | ✓ |
| ssTaxable = 11083 | 11,083 | 11,083 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdPrimary = 0 | 0 | 0 | ✓ |
| rmdSpouse = 0 | 0 | 0 | ✓ |
| rmdReinvested = 0 | 0 | 0 | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 51883 | 51,883 | 51,884 | ✓ |
| withdrawalForSpending = 51883 | 51,883 | 51,884 | ✓ |
| capGains = 0 | 0 | 0 | ✓ |
| agi = 11083 | 11,083 | 11,083 | ✓ |
| fedTax = 0 | 0 | 0 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 0 | 0 | 0 | ✓ |
| drawdownTax = 0 | 0 | 0 | ✓ |
| taxAwareDrawdown = 0 | 0 | 0 | ✓ |
| medicareCost = 10631 | 10,631 | 10,631 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| healthcare = 0 | 0 | 0 | ✓ |
| acaSubsidy = 0 | 0 | 0 | ✓ |
| acaBenchmark = 0 | 0 | 0 | ✓ |
| acaNetPremium = 0 | 0 | 0 | ✓ |
| acaFPLPercent = 0 | 0 | 0 | ✓ |
| expenses = 151845 | 151,845 | 151,845 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| preTax = 0 | 0 | 0 | ✓ |
| preTaxPrimary = 0 | 0 | 0 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 3082084 | 3,082,084 | 3,082,084 | ✓ |
| taxable = 0 | 0 | 0 | ✓ |
| taxableInv = 0 | 0 | 0 | ✓ |
| hsa = 0 | 0 | 0 | ✓ |
| netWorth = 3082084 | 3,082,084 | 3,082,084 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === false | true | true | ✓ |
| rmdRequired === false | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2062 | 2,062 | 2,062 | ✓ |
| age = 95 | 95 | 95 | ✓ |
| spAge = 93 | 93 | 93 | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| totalInc = 148536 | 148,536 | 148,536 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| hsaContrib = 0 | 0 | 0 | ✓ |
| cashInterest = 0 | 0 | 0 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 148536 | 148,536 | 148,536 | ✓ |
| ssTaxable = 31728 | 31,728 | 31,728 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdPrimary = 0 | 0 | 0 | ✓ |
| rmdSpouse = 0 | 0 | 0 | ✓ |
| rmdReinvested = 0 | 0 | 0 | ✓ |
| rothConv = 0 | 0 | 0 | ✓ |
| qcd = 0 | 0 | 0 | ✓ |
| charityTotal = 0 | 0 | 0 | ✓ |
| isItemizing = 0 | 0 | 0 | ✓ |
| isBunchYear = false | false | false | ✓ |
| withdrawal = 134718 | 134,718 | 134,718 | ✓ |
| withdrawalForSpending = 134718 | 134,718 | 134,718 | ✓ |
| capGains = 0 | 0 | 0 | ✓ |
| agi = 31728 | 31,728 | 31,728 | ✓ |
| fedTax = 0 | 0 | 0 | ✓ |
| stateTax = 0 | 0 | 0 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| qbiDeduction = 0 | 0 | 0 | ✓ |
| conversionTax = 0 | 0 | 0 | ✓ |
| tax = 0 | 0 | 0 | ✓ |
| drawdownTax = 0 | 0 | 0 | ✓ |
| taxAwareDrawdown = 0 | 0 | 0 | ✓ |
| medicareCost = 28206 | 28,206 | 28,206 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| healthcare = 0 | 0 | 0 | ✓ |
| acaSubsidy = 0 | 0 | 0 | ✓ |
| acaBenchmark = 0 | 0 | 0 | ✓ |
| acaNetPremium = 0 | 0 | 0 | ✓ |
| acaFPLPercent = 0 | 0 | 0 | ✓ |
| expenses = 283254 | 283,254 | 283,254 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| preTax = 0 | 0 | 0 | ✓ |
| preTaxPrimary = 0 | 0 | 0 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 8372523 | 8,372,523 | 8,372,523 | ✓ |
| taxable = 0 | 0 | 0 | ✓ |
| taxableInv = 0 | 0 | 0 | ✓ |
| hsa = 0 | 0 | 0 | ✓ |
| netWorth = 8372523 | 8,372,523 | 8,372,523 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === false | true | true | ✓ |
| rmdRequired === false | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2026 | 2,026 | 2,026 | ✓ |
| age = 63 | 63 | 63 | ✓ |
| spAge = 61 | 61 | 61 | ✓ |
| earned = 208000 | 208,000 | 208,000 | ✓ |
| primaryEarned = 130000 | 130,000 | 130,000 | ✓ |
| spouseEarned = 78000 | 78,000 | 78,000 | ✓ |
| savings = 32000 | 32,000 | 32,000 | ✓ |
| cashInterest = 4500 | 4,500 | 4,500 | ✓ |
| cash = 104500 | 104,500 | 104,500 | ✓ |
| agi = 193300 | 193,300 | 193,300 | ✓ |
| fedTax = 24866 | 24,866 | 24,866 | ✓ |
| stateTax = 9665 | 9,665 | 9,665 | ✓ |
| fica = 15912 | 15,912 | 15,912 | ✓ |
| tax = 50443 | 50,443 | 50,443 | ✓ |
| debtPayments = 21600 | 21,600 | 21,600 | ✓ |
| debtInterest = 7137 | 7,137 | 7,137 | ✓ |
| debtPrincipalPaid = 14463 | 14,463 | 14,463 | ✓ |
| expenses = 103600 | 103,600 | 103,600 | ✓ |
| preTax = 1037044 | 1,037,044 | 1,037,044 | ✓ |
| preTaxPrimary = 1037044 | 1,037,044 | 1,037,044 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 276060 | 276,060 | 276,060 | ✓ |
| taxableInv = 219136 | 219,136 | 219,136 | ✓ |
| netWorth = 1636740 | 1,636,740 | 1,636,740 | ✓ |
| pension = 0 | 0 | 0 | ✓ |
| ss = 0 | 0 | 0 | ✓ |
| ssTaxable = 0 | 0 | 0 | ✓ |
| ssdi = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdPrimary = 0 | 0 | 0 | ✓ |
| rmdSpouse = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| ltcCost = 0 | 0 | 0 | ✓ |
| medicareCost = 0 | 0 | 0 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| retired === null | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === false | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2028 | 2,028 | 2,028 | ✓ |
| age = 65 | 65 | 65 | ✓ |
| spAge = 63 | 63 | 63 | ✓ |
| earned = 81151 | 81,151 | 81,151 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 81151 | 81,151 | 81,151 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| cashInterest = 4055 | 4,055 | 4,055 | ✓ |
| cash = 94163 | 94,163 | 94,163 | ✓ |
| agi = 107715 | 107,715 | 107,715 | ✓ |
| fedTax = 7372 | 7,372 | 7,372 | ✓ |
| stateTax = 5386 | 5,386 | 5,386 | ✓ |
| fica = 6208 | 6,208 | 6,208 | ✓ |
| tax = 18966 | 18,966 | 18,966 | ✓ |
| pension = 30000 | 30,000 | 30,000 | ✓ |
| pensionTaxable = 30000 | 30,000 | 30,000 | ✓ |
| ss = 0 | 0 | 0 | ✓ |
| ssTaxable = 0 | 0 | 0 | ✓ |
| ssdi = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdPrimary = 0 | 0 | 0 | ✓ |
| rmdSpouse = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| medicareCost = 2685 | 2,685 | 2,685 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| ltcCost = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| debtPayments = 21600 | 21,600 | 21,600 | ✓ |
| debtInterest = 5934 | 5,934 | 5,934 | ✓ |
| debtPrincipalPaid = 15666 | 15,666 | 15,666 | ✓ |
| expenses = 111279 | 111,279 | 111,279 | ✓ |
| preTax = 1209733 | 1,209,733 | 1,209,733 | ✓ |
| preTaxPrimary = 1209733 | 1,209,733 | 1,209,733 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 325403 | 325,403 | 325,403 | ✓ |
| taxableInv = 256494 | 256,494 | 256,494 | ✓ |
| netWorth = 1885793 | 1,885,793 | 1,885,793 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === false | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2033 | 2,033 | 2,033 | ✓ |
| age = 70 | 70 | 70 | ✓ |
| spAge = 68 | 68 | 68 | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| cashInterest = 0 | 0 | 0 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| agi = 86562 | 86,562 | 86,562 | ✓ |
| fedTax = 2870 | 2,870 | 2,870 | ✓ |
| stateTax = 764 | 764 | 764 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| tax = 3634 | 3,634 | 3,634 | ✓ |
| pension = 33122 | 33,122 | 33,122 | ✓ |
| pensionTaxable = 33122 | 33,122 | 33,122 | ✓ |
| ss = 71292 | 71,292 | 71,292 | ✓ |
| ssTaxable = 39177 | 39,177 | 39,177 | ✓ |
| ssdi = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdPrimary = 0 | 0 | 0 | ✓ |
| rmdSpouse = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| medicareCost = 6853 | 6,853 | 6,853 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| ltcCost = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| capGains = 14263 | 14,263 | 14,263 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| withdrawal = 28527 | 28,527 | 28,527 | ✓ |
| withdrawalForSpending = 28527 | 28,527 | 28,527 | ✓ |
| debtPayments = 21600 | 21,600 | 21,600 | ✓ |
| debtInterest = 2472 | 2,472 | 2,472 | ✓ |
| debtPrincipalPaid = 19128 | 19,128 | 19,128 | ✓ |
| expenses = 129303 | 129,303 | 129,303 | ✓ |
| preTax = 1696714 | 1,696,714 | 1,696,714 | ✓ |
| preTaxPrimary = 1696714 | 1,696,714 | 1,696,714 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 456395 | 456,395 | 456,395 | ✓ |
| taxableInv = 107001 | 107,001 | 107,001 | ✓ |
| netWorth = 2260110 | 2,260,110 | 2,260,110 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === false | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2035 | 2,035 | 2,035 | ✓ |
| age = 72 | 72 | 72 | ✓ |
| spAge = 70 | 70 | 70 | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| cashInterest = 0 | 0 | 0 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| agi = 93065 | 93,065 | 93,065 | ✓ |
| fedTax = 3099 | 3,099 | 3,099 | ✓ |
| stateTax = 945 | 945 | 945 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| tax = 4044 | 4,044 | 4,044 | ✓ |
| pension = 34461 | 34,461 | 34,461 | ✓ |
| pensionTaxable = 34461 | 34,461 | 34,461 | ✓ |
| ss = 74172 | 74,172 | 74,172 | ✓ |
| ssTaxable = 42826 | 42,826 | 42,826 | ✓ |
| ssdi = 0 | 0 | 0 | ✓ |
| rmd = 0 | 0 | 0 | ✓ |
| rmdPrimary = 0 | 0 | 0 | ✓ |
| rmdSpouse = 0 | 0 | 0 | ✓ |
| rmdRequired = false | false | false | ✓ |
| medicareCost = 7555 | 7,555 | 7,555 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| ltcCost = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| capGains = 15778 | 15,778 | 15,778 | ✓ |
| cgTax = 0 | 0 | 0 | ✓ |
| withdrawal = 31555 | 31,555 | 31,555 | ✓ |
| withdrawalForSpending = 31555 | 31,555 | 31,555 | ✓ |
| debtPayments = 21600 | 21,600 | 21,600 | ✓ |
| debtInterest = 882 | 882 | 882 | ✓ |
| debtPrincipalPaid = 20718 | 20,718 | 20,718 | ✓ |
| expenses = 136146 | 136,146 | 136,146 | ✓ |
| preTax = 1942567 | 1,942,567 | 1,942,567 | ✓ |
| preTaxPrimary = 1942567 | 1,942,567 | 1,942,567 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 522527 | 522,527 | 522,527 | ✓ |
| taxableInv = 54384 | 54,384 | 54,384 | ✓ |
| netWorth = 2519478 | 2,519,478 | 2,519,478 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === false | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2038 | 2,038 | 2,038 | ✓ |
| age = 75 | 75 | 75 | ✓ |
| spAge = 73 | 73 | 73 | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| cashInterest = 0 | 0 | 0 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| agi = 202263 | 202,263 | 202,263 | ✓ |
| fedTax = 17744 | 17,744 | 17,744 | ✓ |
| stateTax = 6178 | 6,178 | 6,178 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| tax = 23922 | 23,922 | 23,922 | ✓ |
| pension = 36570 | 36,570 | 36,570 | ✓ |
| pensionTaxable = 36570 | 36,570 | 36,570 | ✓ |
| ss = 78712 | 78,712 | 78,712 | ✓ |
| ssTaxable = 66905 | 66,905 | 66,905 | ✓ |
| ssdi = 0 | 0 | 0 | ✓ |
| rmd = 90408 | 90,408 | 90,408 | ✓ |
| rmdPrimary = 90408 | 90,408 | 90,408 | ✓ |
| rmdSpouse = 0 | 0 | 0 | ✓ |
| rmdRequired = true | true | true | ✓ |
| rmdReinvested = 90408 | 90,408 | 90,408 | ✓ |
| medicareCost = 8746 | 8,746 | 8,746 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| ltcCost = 0 | 0 | 0 | ✓ |
| niitTax = 0 | 0 | 0 | ✓ |
| capGains = 8380 | 8,380 | 8,380 | ✓ |
| cgTax = 1257 | 1,257 | 1,257 | ✓ |
| withdrawal = 107168 | 107,168 | 107,168 | ✓ |
| withdrawalForSpending = 16760 | 16,760 | 16,760 | ✓ |
| debtPayments = 0 | 0 | 0 | ✓ |
| debtInterest = 0 | 0 | 0 | ✓ |
| debtPrincipalPaid = 0 | 0 | 0 | ✓ |
| expenses = 108122 | 108,122 | 108,122 | ✓ |
| preTax = 2282992 | 2,282,992 | 2,282,992 | ✓ |
| preTaxPrimary = 2282992 | 2,282,992 | 2,282,992 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 640118 | 640,118 | 640,118 | ✓ |
| taxableInv = 106329 | 106,329 | 106,329 | ✓ |
| netWorth = 3029439 | 3,029,439 | 3,029,439 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === true | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| year = 2058 | 2,058 | 2,058 | ✓ |
| age = 95 | 95 | 95 | ✓ |
| spAge = 93 | 93 | 93 | ✓ |
| earned = 0 | 0 | 0 | ✓ |
| primaryEarned = 0 | 0 | 0 | ✓ |
| spouseEarned = 0 | 0 | 0 | ✓ |
| savings = 0 | 0 | 0 | ✓ |
| cashInterest = 0 | 0 | 0 | ✓ |
| cash = 0 | 0 | 0 | ✓ |
| agi = 457724 | 457,724 | 457,724 | ✓ |
| fedTax = 51446 | 51,446 | 51,446 | ✓ |
| stateTax = 17038 | 17,038 | 17,038 | ✓ |
| fica = 0 | 0 | 0 | ✓ |
| tax = 68484 | 68,484 | 68,484 | ✓ |
| pension = 54341 | 54,341 | 54,341 | ✓ |
| pensionTaxable = 54341 | 54,341 | 54,341 | ✓ |
| ss = 116962 | 116,962 | 116,962 | ✓ |
| ssTaxable = 99418 | 99,418 | 99,418 | ✓ |
| ssdi = 0 | 0 | 0 | ✓ |
| rmd = 243474 | 243,474 | 243,474 | ✓ |
| rmdPrimary = 243474 | 243,474 | 243,474 | ✓ |
| rmdSpouse = 0 | 0 | 0 | ✓ |
| rmdRequired = true | true | true | ✓ |
| rmdReinvested = 243474 | 243,474 | 243,474 | ✓ |
| medicareCost = 23205 | 23,205 | 23,205 | ✓ |
| irmaa = 0 | 0 | 0 | ✓ |
| irmaaBracket = 0 | 0 | 0 | ✓ |
| ltcCost = 0 | 0 | 0 | ✓ |
| niitTax = 2299 | 2,299 | 2,299 | ✓ |
| capGains = 60491 | 60,491 | 60,491 | ✓ |
| cgTax = 9074 | 9,074 | 9,074 | ✓ |
| withdrawal = 364456 | 364,456 | 364,456 | ✓ |
| withdrawalForSpending = 120982 | 120,982 | 120,982 | ✓ |
| debtPayments = 0 | 0 | 0 | ✓ |
| debtInterest = 0 | 0 | 0 | ✓ |
| debtPrincipalPaid = 0 | 0 | 0 | ✓ |
| expenses = 223804 | 223,804 | 223,804 | ✓ |
| preTax = 2058085 | 2,058,085 | 2,058,085 | ✓ |
| preTaxPrimary = 2058085 | 2,058,085 | 2,058,085 | ✓ |
| preTaxSpouse = 0 | 0 | 0 | ✓ |
| roth = 2477054 | 2,477,054 | 2,477,054 | ✓ |
| taxableInv = 1620722 | 1,620,722 | 1,620,722 | ✓ |
| netWorth = 6155861 | 6,155,861 | 6,155,861 | ✓ |
| retired === true | true | true | ✓ |
| isSingle === null | true | true | ✓ |
| healthcareSource === null | true | true | ✓ |
| taxConvergenceFlag === false | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| projection.length = 32 | 32 | 32 | ✓ |
| Y1.year = Y0.year + 1 | 2,027 | 2,027 | ✓ |
| Y2.year = Y1.year + 1 | 2,028 | 2,028 | ✓ |
| Y3.year = Y2.year + 1 | 2,029 | 2,029 | ✓ |
| Y4.year = Y3.year + 1 | 2,030 | 2,030 | ✓ |
| Y5.year = Y4.year + 1 | 2,031 | 2,031 | ✓ |
| Y6.year = Y5.year + 1 | 2,032 | 2,032 | ✓ |
| Y7.year = Y6.year + 1 | 2,033 | 2,033 | ✓ |
| Y8.year = Y7.year + 1 | 2,034 | 2,034 | ✓ |
| Y9.year = Y8.year + 1 | 2,035 | 2,035 | ✓ |
| Y10.year = Y9.year + 1 | 2,036 | 2,036 | ✓ |
| Y11.year = Y10.year + 1 | 2,037 | 2,037 | ✓ |
| Y12.year = Y11.year + 1 | 2,038 | 2,038 | ✓ |
| Y13.year = Y12.year + 1 | 2,039 | 2,039 | ✓ |
| Y14.year = Y13.year + 1 | 2,040 | 2,040 | ✓ |
| Y15.year = Y14.year + 1 | 2,041 | 2,041 | ✓ |
| Y16.year = Y15.year + 1 | 2,042 | 2,042 | ✓ |
| Y17.year = Y16.year + 1 | 2,043 | 2,043 | ✓ |
| Y18.year = Y17.year + 1 | 2,044 | 2,044 | ✓ |
| Y19.year = Y18.year + 1 | 2,045 | 2,045 | ✓ |
| Y20.year = Y19.year + 1 | 2,046 | 2,046 | ✓ |
| Y21.year = Y20.year + 1 | 2,047 | 2,047 | ✓ |
| Y22.year = Y21.year + 1 | 2,048 | 2,048 | ✓ |
| Y23.year = Y22.year + 1 | 2,049 | 2,049 | ✓ |
| Y24.year = Y23.year + 1 | 2,050 | 2,050 | ✓ |
| Y25.year = Y24.year + 1 | 2,051 | 2,051 | ✓ |
| Y26.year = Y25.year + 1 | 2,052 | 2,052 | ✓ |
| Y27.year = Y26.year + 1 | 2,053 | 2,053 | ✓ |
| Y28.year = Y27.year + 1 | 2,054 | 2,054 | ✓ |
| Y29.year = Y28.year + 1 | 2,055 | 2,055 | ✓ |
| Y30.year = Y29.year + 1 | 2,056 | 2,056 | ✓ |
| Y31.year = Y30.year + 1 | 2,057 | 2,057 | ✓ |
| Y1.age = Y0.age + 1 | 65 | 65 | ✓ |
| Y2.age = Y1.age + 1 | 66 | 66 | ✓ |
| Y3.age = Y2.age + 1 | 67 | 67 | ✓ |
| Y4.age = Y3.age + 1 | 68 | 68 | ✓ |
| Y5.age = Y4.age + 1 | 69 | 69 | ✓ |
| Y6.age = Y5.age + 1 | 70 | 70 | ✓ |
| Y7.age = Y6.age + 1 | 71 | 71 | ✓ |
| Y8.age = Y7.age + 1 | 72 | 72 | ✓ |
| Y9.age = Y8.age + 1 | 73 | 73 | ✓ |
| Y10.age = Y9.age + 1 | 74 | 74 | ✓ |
| Y11.age = Y10.age + 1 | 75 | 75 | ✓ |
| Y12.age = Y11.age + 1 | 76 | 76 | ✓ |
| Y13.age = Y12.age + 1 | 77 | 77 | ✓ |
| Y14.age = Y13.age + 1 | 78 | 78 | ✓ |
| Y15.age = Y14.age + 1 | 79 | 79 | ✓ |
| Y16.age = Y15.age + 1 | 80 | 80 | ✓ |
| Y17.age = Y16.age + 1 | 81 | 81 | ✓ |
| Y18.age = Y17.age + 1 | 82 | 82 | ✓ |
| Y19.age = Y18.age + 1 | 83 | 83 | ✓ |
| Y20.age = Y19.age + 1 | 84 | 84 | ✓ |
| Y21.age = Y20.age + 1 | 85 | 85 | ✓ |
| Y22.age = Y21.age + 1 | 86 | 86 | ✓ |
| Y23.age = Y22.age + 1 | 87 | 87 | ✓ |
| Y24.age = Y23.age + 1 | 88 | 88 | ✓ |
| Y25.age = Y24.age + 1 | 89 | 89 | ✓ |
| Y26.age = Y25.age + 1 | 90 | 90 | ✓ |
| Y27.age = Y26.age + 1 | 91 | 91 | ✓ |
| Y28.age = Y27.age + 1 | 92 | 92 | ✓ |
| Y29.age = Y28.age + 1 | 93 | 93 | ✓ |
| Y30.age = Y29.age + 1 | 94 | 94 | ✓ |
| Y31.age = Y30.age + 1 | 95 | 95 | ✓ |
| Y0 earned = primaryEarned + spouseEarned | 182,000 | 182,000 | ✓ |
| Y1 earned = primaryEarned + spouseEarned | 73,440 | 73,440 | ✓ |
| Y2 earned = primaryEarned + spouseEarned | 74,909 | 74,909 | ✓ |
| Y3 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y4 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y5 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y6 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y7 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y8 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y9 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y10 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y11 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y12 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y13 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y14 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y15 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y16 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y17 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y18 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y19 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y20 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y21 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y22 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y23 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y24 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y25 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y26 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y27 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y28 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y29 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y30 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y31 earned = primaryEarned + spouseEarned | 0 | 0 | ✓ |
| Y0 tax = fedTax + stateTax + fica | 31,125 | 31,125 | ✓ |
| Y1 tax = fedTax + stateTax + fica | 8,290 | 8,290 | ✓ |
| Y2 tax = fedTax + stateTax + fica | 7,632 | 7,632 | ✓ |
| Y3 tax = fedTax + stateTax + fica | 0 | 0 | ✓ |
| Y4 tax = fedTax + stateTax + fica | 2,982 | 2,982 | ✓ |
| Y5 tax = fedTax + stateTax + fica | 3,210 | 3,210 | ✓ |
| Y6 tax = fedTax + stateTax + fica | 3,624 | 3,624 | ✓ |
| Y7 tax = fedTax + stateTax + fica | 4,044 | 4,044 | ✓ |
| Y8 tax = fedTax + stateTax + fica | 4,495 | 4,495 | ✓ |
| Y9 tax = fedTax + stateTax + fica | 4,939 | 4,939 | ✓ |
| Y10 tax = fedTax + stateTax + fica | 5,424 | 5,424 | ✓ |
| Y11 tax = fedTax + stateTax + fica | 5,926 | 5,926 | ✓ |
| Y12 tax = fedTax + stateTax + fica | 6,432 | 6,432 | ✓ |
| Y13 tax = fedTax + stateTax + fica | 6,981 | 6,981 | ✓ |
| Y14 tax = fedTax + stateTax + fica | 7,544 | 7,544 | ✓ |
| Y15 tax = fedTax + stateTax + fica | 8,121 | 8,121 | ✓ |
| Y16 tax = fedTax + stateTax + fica | 8,737 | 8,737 | ✓ |
| Y17 tax = fedTax + stateTax + fica | 9,084 | 9,084 | ✓ |
| Y18 tax = fedTax + stateTax + fica | 9,397 | 9,397 | ✓ |
| Y19 tax = fedTax + stateTax + fica | 9,735 | 9,735 | ✓ |
| Y20 tax = fedTax + stateTax + fica | 10,158 | 10,158 | ✓ |
| Y21 tax = fedTax + stateTax + fica | 10,658 | 10,658 | ✓ |
| Y22 tax = fedTax + stateTax + fica | 11,187 | 11,187 | ✓ |
| Y23 tax = fedTax + stateTax + fica | 11,733 | 11,733 | ✓ |
| Y24 tax = fedTax + stateTax + fica | 12,197 | 12,197 | ✓ |
| Y25 tax = fedTax + stateTax + fica | 12,781 | 12,781 | ✓ |
| Y26 tax = fedTax + stateTax + fica | 13,265 | 13,265 | ✓ |
| Y27 tax = fedTax + stateTax + fica | 13,743 | 13,743 | ✓ |
| Y28 tax = fedTax + stateTax + fica | 14,216 | 14,216 | ✓ |
| Y29 tax = fedTax + stateTax + fica | 14,678 | 14,678 | ✓ |
| Y30 tax = fedTax + stateTax + fica | 15,007 | 15,007 | ✓ |
| Y31 tax = fedTax + stateTax + fica | 15,589 | 15,589 | ✓ |
| Y0 netWorth = sum of buckets | 1,153,460 | 1,153,460 | ✓ |
| Y1 netWorth = sum of buckets | 1,213,008 | 1,213,008 | ✓ |
| Y2 netWorth = sum of buckets | 1,275,263 | 1,275,263 | ✓ |
| Y3 netWorth = sum of buckets | 1,300,389 | 1,300,389 | ✓ |
| Y4 netWorth = sum of buckets | 1,321,689 | 1,321,689 | ✓ |
| Y5 netWorth = sum of buckets | 1,368,691 | 1,368,691 | ✓ |
| Y6 netWorth = sum of buckets | 1,416,502 | 1,416,502 | ✓ |
| Y7 netWorth = sum of buckets | 1,465,095 | 1,465,095 | ✓ |
| Y8 netWorth = sum of buckets | 1,514,410 | 1,514,410 | ✓ |
| Y9 netWorth = sum of buckets | 1,564,419 | 1,564,419 | ✓ |
| Y10 netWorth = sum of buckets | 1,615,035 | 1,615,035 | ✓ |
| Y11 netWorth = sum of buckets | 1,666,190 | 1,666,190 | ✓ |
| Y12 netWorth = sum of buckets | 1,717,821 | 1,717,821 | ✓ |
| Y13 netWorth = sum of buckets | 1,769,816 | 1,769,816 | ✓ |
| Y14 netWorth = sum of buckets | 1,822,082 | 1,822,082 | ✓ |
| Y15 netWorth = sum of buckets | 1,874,513 | 1,874,513 | ✓ |
| Y16 netWorth = sum of buckets | 1,926,968 | 1,926,968 | ✓ |
| Y17 netWorth = sum of buckets | 1,979,620 | 1,979,620 | ✓ |
| Y18 netWorth = sum of buckets | 2,032,399 | 2,032,399 | ✓ |
| Y19 netWorth = sum of buckets | 2,085,161 | 2,085,161 | ✓ |
| Y20 netWorth = sum of buckets | 2,137,683 | 2,137,683 | ✓ |
| Y21 netWorth = sum of buckets | 2,189,733 | 2,189,733 | ✓ |
| Y22 netWorth = sum of buckets | 2,241,109 | 2,241,109 | ✓ |
| Y23 netWorth = sum of buckets | 2,291,602 | 2,291,602 | ✓ |
| Y24 netWorth = sum of buckets | 2,341,085 | 2,341,085 | ✓ |
| Y25 netWorth = sum of buckets | 2,389,207 | 2,389,207 | ✓ |
| Y26 netWorth = sum of buckets | 2,435,815 | 2,435,815 | ✓ |
| Y27 netWorth = sum of buckets | 2,480,646 | 2,480,646 | ✓ |
| Y28 netWorth = sum of buckets | 2,523,401 | 2,523,401 | ✓ |
| Y29 netWorth = sum of buckets | 2,563,771 | 2,563,771 | ✓ |
| Y30 netWorth = sum of buckets | 2,601,542 | 2,601,542 | ✓ |
| Y31 netWorth = sum of buckets | 2,636,070 | 2,636,070 | ✓ |
| Y0 retired === null | true | true | ✓ |
| all years Y1+ retired === true | true | true | ✓ |
| Y0 earned > 0 (both working) | true | true | ✓ |
| Y1 earned > 0 (spouse continues working) | true | true | ✓ |
| Y2 earned > 0 (spouse continues working) | true | true | ✓ |
| all years Y3+ earned = 0 | true | true | ✓ |
| all years Y0-Y2 ss = 0 (pre-claim) | true | true | ✓ |
| all years Y3+ ss > 0 (post-primary-claim) | true | true | ✓ |
| all years Y0-Y10 rmd = 0 (pre-trigger) | true | true | ✓ |
| all years Y0-Y10 rmdRequired === false (pre-trigger) | true | true | ✓ |
| all years Y11+ rmdRequired === true (post-trigger) | true | true | ✓ |
| all years rothConv = 0 | true | true | ✓ |
| all years hsaContrib = 0 | true | true | ✓ |
| all years charityTotal = 0 | true | true | ✓ |
| all years stateTax = 0 | true | true | ✓ |
| all years fedTax >= 0 | true | true | ✓ |
| all years cgTax >= 0 | true | true | ✓ |
| all years niitTax >= 0 | true | true | ✓ |
| roth bucket monotonically non-decreasing | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Y0 rmd === 0 | true | true | ✓ |
| Y1 rmd === 0 | true | true | ✓ |
| Y2 rmd === 0 | true | true | ✓ |
| Y3 rmd === 0 | true | true | ✓ |
| Y4 rmd === 0 | true | true | ✓ |
| Y5 rmd === 0 | true | true | ✓ |
| Y6 rmd === 0 | true | true | ✓ |
| Y7 rmd === 0 | true | true | ✓ |
| Y8 rmd === 0 | true | true | ✓ |
| Y9 rmd === 0 | true | true | ✓ |
| Y10 rmd === 0 | true | true | ✓ |
| Y11 rmd === 0 | true | true | ✓ |
| Y12 rmd === 0 | true | true | ✓ |
| Y13 rmd === 0 | true | true | ✓ |
| Y14 rmd === 0 | true | true | ✓ |
| Y15 rmd === 0 | true | true | ✓ |
| Y16 rmd === 0 | true | true | ✓ |
| Y17 rmd === 0 | true | true | ✓ |
| Y18 rmd === 0 | true | true | ✓ |
| Y19 rmd === 0 | true | true | ✓ |
| Y20 rmd === 0 | true | true | ✓ |
| Y21 rmd === 0 | true | true | ✓ |
| Y22 rmd === 0 | true | true | ✓ |
| Y23 rmd === 0 | true | true | ✓ |
| Y24 rmd === 0 | true | true | ✓ |
| Y25 rmd === 0 | true | true | ✓ |
| Y26 rmd === 0 | true | true | ✓ |
| Y27 rmd === 0 | true | true | ✓ |
| Y28 rmd === 0 | true | true | ✓ |
| Y29 rmd === 0 | true | true | ✓ |
| Y30 rmd === 0 | true | true | ✓ |
| Y31 rmd === 0 | true | true | ✓ |
| Y32 rmd === 0 | true | true | ✓ |
| Y33 rmd === 0 | true | true | ✓ |
| Y34 rmd === 0 | true | true | ✓ |
| Y35 rmd === 0 | true | true | ✓ |
| Y36 rmd === 0 | true | true | ✓ |
| Y12 AGI === ssTaxable | true | true | ✓ |
| Y12 fedTax === 0 | true | true | ✓ |
| Y12 stateTax === 0 | true | true | ✓ |
| Y12 withdrawal === expenses − ss | true | true | ✓ |
| Y13 AGI === ssTaxable | true | true | ✓ |
| Y13 fedTax === 0 | true | true | ✓ |
| Y13 stateTax === 0 | true | true | ✓ |
| Y13 withdrawal === expenses − ss | true | true | ✓ |
| Y14 AGI === ssTaxable | true | true | ✓ |
| Y14 fedTax === 0 | true | true | ✓ |
| Y14 stateTax === 0 | true | true | ✓ |
| Y14 withdrawal === expenses − ss | true | true | ✓ |
| Y15 AGI === ssTaxable | true | true | ✓ |
| Y15 fedTax === 0 | true | true | ✓ |
| Y15 stateTax === 0 | true | true | ✓ |
| Y15 withdrawal === expenses − ss | true | true | ✓ |
| Y17 AGI === ssTaxable | true | true | ✓ |
| Y17 fedTax === 0 | true | true | ✓ |
| Y17 stateTax === 0 | true | true | ✓ |
| Y17 withdrawal === expenses − ss | true | true | ✓ |
| Y18 AGI === ssTaxable | true | true | ✓ |
| Y18 fedTax === 0 | true | true | ✓ |
| Y18 stateTax === 0 | true | true | ✓ |
| Y18 withdrawal === expenses − ss | true | true | ✓ |
| Y19 AGI === ssTaxable | true | true | ✓ |
| Y19 fedTax === 0 | true | true | ✓ |
| Y19 stateTax === 0 | true | true | ✓ |
| Y19 withdrawal === expenses − ss | true | true | ✓ |
| Y20 AGI === ssTaxable | true | true | ✓ |
| Y20 fedTax === 0 | true | true | ✓ |
| Y20 stateTax === 0 | true | true | ✓ |
| Y20 withdrawal === expenses − ss | true | true | ✓ |
| Y21 AGI === ssTaxable | true | true | ✓ |
| Y21 fedTax === 0 | true | true | ✓ |
| Y21 stateTax === 0 | true | true | ✓ |
| Y21 withdrawal === expenses − ss | true | true | ✓ |
| Y22 AGI === ssTaxable | true | true | ✓ |
| Y22 fedTax === 0 | true | true | ✓ |
| Y22 stateTax === 0 | true | true | ✓ |
| Y22 withdrawal === expenses − ss | true | true | ✓ |
| Y23 AGI === ssTaxable | true | true | ✓ |
| Y23 fedTax === 0 | true | true | ✓ |
| Y23 stateTax === 0 | true | true | ✓ |
| Y23 withdrawal === expenses − ss | true | true | ✓ |
| Y24 AGI === ssTaxable | true | true | ✓ |
| Y24 fedTax === 0 | true | true | ✓ |
| Y24 stateTax === 0 | true | true | ✓ |
| Y24 withdrawal === expenses − ss | true | true | ✓ |
| Y25 AGI === ssTaxable | true | true | ✓ |
| Y25 fedTax === 0 | true | true | ✓ |
| Y25 stateTax === 0 | true | true | ✓ |
| Y25 withdrawal === expenses − ss | true | true | ✓ |
| Y26 AGI === ssTaxable | true | true | ✓ |
| Y26 fedTax === 0 | true | true | ✓ |
| Y26 stateTax === 0 | true | true | ✓ |
| Y26 withdrawal === expenses − ss | true | true | ✓ |
| Y27 AGI === ssTaxable | true | true | ✓ |
| Y27 fedTax === 0 | true | true | ✓ |
| Y27 stateTax === 0 | true | true | ✓ |
| Y27 withdrawal === expenses − ss | true | true | ✓ |
| Y28 AGI === ssTaxable | true | true | ✓ |
| Y28 fedTax === 0 | true | true | ✓ |
| Y28 stateTax === 0 | true | true | ✓ |
| Y28 withdrawal === expenses − ss | true | true | ✓ |
| Y29 AGI === ssTaxable | true | true | ✓ |
| Y29 fedTax === 0 | true | true | ✓ |
| Y29 stateTax === 0 | true | true | ✓ |
| Y29 withdrawal === expenses − ss | true | true | ✓ |
| Y30 AGI === ssTaxable | true | true | ✓ |
| Y30 fedTax === 0 | true | true | ✓ |
| Y30 stateTax === 0 | true | true | ✓ |
| Y30 withdrawal === expenses − ss | true | true | ✓ |
| Y31 AGI === ssTaxable | true | true | ✓ |
| Y31 fedTax === 0 | true | true | ✓ |
| Y31 stateTax === 0 | true | true | ✓ |
| Y31 withdrawal === expenses − ss | true | true | ✓ |
| Y32 AGI === ssTaxable | true | true | ✓ |
| Y32 fedTax === 0 | true | true | ✓ |
| Y32 stateTax === 0 | true | true | ✓ |
| Y32 withdrawal === expenses − ss | true | true | ✓ |
| Y33 AGI === ssTaxable | true | true | ✓ |
| Y33 fedTax === 0 | true | true | ✓ |
| Y33 stateTax === 0 | true | true | ✓ |
| Y33 withdrawal === expenses − ss | true | true | ✓ |
| Y34 AGI === ssTaxable | true | true | ✓ |
| Y34 fedTax === 0 | true | true | ✓ |
| Y34 stateTax === 0 | true | true | ✓ |
| Y34 withdrawal === expenses − ss | true | true | ✓ |
| Y35 AGI === ssTaxable | true | true | ✓ |
| Y35 fedTax === 0 | true | true | ✓ |
| Y35 stateTax === 0 | true | true | ✓ |
| Y35 withdrawal === expenses − ss | true | true | ✓ |
| Y1 ss === 0 | true | true | ✓ |
| Y1 ssTaxable === 0 | true | true | ✓ |
| Y2 ss === 0 | true | true | ✓ |
| Y2 ssTaxable === 0 | true | true | ✓ |
| Y4 ss === 0 | true | true | ✓ |
| Y4 ssTaxable === 0 | true | true | ✓ |
| Y6 ss === 0 | true | true | ✓ |
| Y6 ssTaxable === 0 | true | true | ✓ |
| Y7 ss === 0 | true | true | ✓ |
| Y7 ssTaxable === 0 | true | true | ✓ |
| Y8 ss === 0 | true | true | ✓ |
| Y8 ssTaxable === 0 | true | true | ✓ |
| Y9 ss === 0 | true | true | ✓ |
| Y9 ssTaxable === 0 | true | true | ✓ |
| Y12 ss > Y11 ss | true | true | ✓ |
| Y13 ss > Y12 ss | true | true | ✓ |
| Y14 ss > Y13 ss | true | true | ✓ |
| Y15 ss > Y14 ss | true | true | ✓ |
| Y17 ss > Y16 ss | true | true | ✓ |
| Y18 ss > Y17 ss | true | true | ✓ |
| Y19 ss > Y18 ss | true | true | ✓ |
| Y20 ss > Y19 ss | true | true | ✓ |
| Y21 ss > Y20 ss | true | true | ✓ |
| Y22 ss > Y21 ss | true | true | ✓ |
| Y23 ss > Y22 ss | true | true | ✓ |
| Y24 ss > Y23 ss | true | true | ✓ |
| Y25 ss > Y24 ss | true | true | ✓ |
| Y26 ss > Y25 ss | true | true | ✓ |
| Y27 ss > Y26 ss | true | true | ✓ |
| Y28 ss > Y27 ss | true | true | ✓ |
| Y29 ss > Y28 ss | true | true | ✓ |
| Y30 ss > Y29 ss | true | true | ✓ |
| Y31 ss > Y30 ss | true | true | ✓ |
| Y32 ss > Y31 ss | true | true | ✓ |
| Y33 ss > Y32 ss | true | true | ✓ |
| Y34 ss > Y33 ss | true | true | ✓ |
| Y35 ss > Y34 ss | true | true | ✓ |
| Y8 medicareCost > Y7 medicareCost | true | true | ✓ |
| Y9 medicareCost > Y8 medicareCost | true | true | ✓ |
| Y10 medicareCost > Y9 medicareCost | true | true | ✓ |
| Y12 medicareCost > Y11 medicareCost | true | true | ✓ |
| Y13 medicareCost > Y12 medicareCost | true | true | ✓ |
| Y14 medicareCost > Y13 medicareCost | true | true | ✓ |
| Y15 medicareCost > Y14 medicareCost | true | true | ✓ |
| Y17 medicareCost > Y16 medicareCost | true | true | ✓ |
| Y18 medicareCost > Y17 medicareCost | true | true | ✓ |
| Y19 medicareCost > Y18 medicareCost | true | true | ✓ |
| Y20 medicareCost > Y19 medicareCost | true | true | ✓ |
| Y21 medicareCost > Y20 medicareCost | true | true | ✓ |
| Y22 medicareCost > Y21 medicareCost | true | true | ✓ |
| Y23 medicareCost > Y22 medicareCost | true | true | ✓ |
| Y24 medicareCost > Y23 medicareCost | true | true | ✓ |
| Y25 medicareCost > Y24 medicareCost | true | true | ✓ |
| Y26 medicareCost > Y25 medicareCost | true | true | ✓ |
| Y27 medicareCost > Y26 medicareCost | true | true | ✓ |
| Y28 medicareCost > Y27 medicareCost | true | true | ✓ |
| Y29 medicareCost > Y28 medicareCost | true | true | ✓ |
| Y30 medicareCost > Y29 medicareCost | true | true | ✓ |
| Y31 medicareCost > Y30 medicareCost | true | true | ✓ |
| Y32 medicareCost > Y31 medicareCost | true | true | ✓ |
| Y33 medicareCost > Y32 medicareCost | true | true | ✓ |
| Y34 medicareCost > Y33 medicareCost | true | true | ✓ |
| Y35 medicareCost > Y34 medicareCost | true | true | ✓ |
| taxConvergenceFlag === false across full projection | true | true | ✓ |
| niitTax === 0 from Y8 onward | true | true | ✓ |
| irmaaBracket === 0 across all years | true | true | ✓ |
| rmdRequired === false across all years | true | true | ✓ |
| roth balance > 0 across all years | true | true | ✓ |
| preTax === 0 from Y12 onward | true | true | ✓ |
| age increments by 1 each year | true | true | ✓ |
| spAge increments by 1 each year | true | true | ✓ |
| age[36] === endAge 95 | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| Y0 age === 63 | true | true | ✓ |
| Y0 spAge === 61 | true | true | ✓ |
| Y1 age === 64 | true | true | ✓ |
| Y1 spAge === 62 | true | true | ✓ |
| Y2 age === 65 | true | true | ✓ |
| Y2 spAge === 63 | true | true | ✓ |
| Y3 age === 66 | true | true | ✓ |
| Y3 spAge === 64 | true | true | ✓ |
| Y4 age === 67 | true | true | ✓ |
| Y4 spAge === 65 | true | true | ✓ |
| Y5 age === 68 | true | true | ✓ |
| Y5 spAge === 66 | true | true | ✓ |
| Y6 age === 69 | true | true | ✓ |
| Y6 spAge === 67 | true | true | ✓ |
| Y7 age === 70 | true | true | ✓ |
| Y7 spAge === 68 | true | true | ✓ |
| Y8 age === 71 | true | true | ✓ |
| Y8 spAge === 69 | true | true | ✓ |
| Y9 age === 72 | true | true | ✓ |
| Y9 spAge === 70 | true | true | ✓ |
| Y10 age === 73 | true | true | ✓ |
| Y10 spAge === 71 | true | true | ✓ |
| Y11 age === 74 | true | true | ✓ |
| Y11 spAge === 72 | true | true | ✓ |
| Y12 age === 75 | true | true | ✓ |
| Y12 spAge === 73 | true | true | ✓ |
| Y13 age === 76 | true | true | ✓ |
| Y13 spAge === 74 | true | true | ✓ |
| Y14 age === 77 | true | true | ✓ |
| Y14 spAge === 75 | true | true | ✓ |
| Y15 age === 78 | true | true | ✓ |
| Y15 spAge === 76 | true | true | ✓ |
| Y16 age === 79 | true | true | ✓ |
| Y16 spAge === 77 | true | true | ✓ |
| Y17 age === 80 | true | true | ✓ |
| Y17 spAge === 78 | true | true | ✓ |
| Y18 age === 81 | true | true | ✓ |
| Y18 spAge === 79 | true | true | ✓ |
| Y19 age === 82 | true | true | ✓ |
| Y19 spAge === 80 | true | true | ✓ |
| Y20 age === 83 | true | true | ✓ |
| Y20 spAge === 81 | true | true | ✓ |
| Y21 age === 84 | true | true | ✓ |
| Y21 spAge === 82 | true | true | ✓ |
| Y22 age === 85 | true | true | ✓ |
| Y22 spAge === 83 | true | true | ✓ |
| Y23 age === 86 | true | true | ✓ |
| Y23 spAge === 84 | true | true | ✓ |
| Y24 age === 87 | true | true | ✓ |
| Y24 spAge === 85 | true | true | ✓ |
| Y25 age === 88 | true | true | ✓ |
| Y25 spAge === 86 | true | true | ✓ |
| Y26 age === 89 | true | true | ✓ |
| Y26 spAge === 87 | true | true | ✓ |
| Y27 age === 90 | true | true | ✓ |
| Y27 spAge === 88 | true | true | ✓ |
| Y28 age === 91 | true | true | ✓ |
| Y28 spAge === 89 | true | true | ✓ |
| Y29 age === 92 | true | true | ✓ |
| Y29 spAge === 90 | true | true | ✓ |
| Y30 age === 93 | true | true | ✓ |
| Y30 spAge === 91 | true | true | ✓ |
| Y31 age === 94 | true | true | ✓ |
| Y31 spAge === 92 | true | true | ✓ |
| Y32 age === 95 | true | true | ✓ |
| Y32 spAge === 93 | true | true | ✓ |
| Y0 isSingle === null | true | true | ✓ |
| Y0 primaryDead === false | true | true | ✓ |
| Y0 spouseDead === false | true | true | ✓ |
| Y1 isSingle === null | true | true | ✓ |
| Y1 primaryDead === false | true | true | ✓ |
| Y1 spouseDead === false | true | true | ✓ |
| Y2 isSingle === null | true | true | ✓ |
| Y2 primaryDead === false | true | true | ✓ |
| Y2 spouseDead === false | true | true | ✓ |
| Y3 isSingle === null | true | true | ✓ |
| Y3 primaryDead === false | true | true | ✓ |
| Y3 spouseDead === false | true | true | ✓ |
| Y4 isSingle === null | true | true | ✓ |
| Y4 primaryDead === false | true | true | ✓ |
| Y4 spouseDead === false | true | true | ✓ |
| Y5 isSingle === null | true | true | ✓ |
| Y5 primaryDead === false | true | true | ✓ |
| Y5 spouseDead === false | true | true | ✓ |
| Y6 isSingle === null | true | true | ✓ |
| Y6 primaryDead === false | true | true | ✓ |
| Y6 spouseDead === false | true | true | ✓ |
| Y7 isSingle === null | true | true | ✓ |
| Y7 primaryDead === false | true | true | ✓ |
| Y7 spouseDead === false | true | true | ✓ |
| Y8 isSingle === null | true | true | ✓ |
| Y8 primaryDead === false | true | true | ✓ |
| Y8 spouseDead === false | true | true | ✓ |
| Y9 isSingle === null | true | true | ✓ |
| Y9 primaryDead === false | true | true | ✓ |
| Y9 spouseDead === false | true | true | ✓ |
| Y10 isSingle === null | true | true | ✓ |
| Y10 primaryDead === false | true | true | ✓ |
| Y10 spouseDead === false | true | true | ✓ |
| Y11 isSingle === null | true | true | ✓ |
| Y11 primaryDead === false | true | true | ✓ |
| Y11 spouseDead === false | true | true | ✓ |
| Y12 isSingle === null | true | true | ✓ |
| Y12 primaryDead === false | true | true | ✓ |
| Y12 spouseDead === false | true | true | ✓ |
| Y13 isSingle === null | true | true | ✓ |
| Y13 primaryDead === false | true | true | ✓ |
| Y13 spouseDead === false | true | true | ✓ |
| Y14 isSingle === null | true | true | ✓ |
| Y14 primaryDead === false | true | true | ✓ |
| Y14 spouseDead === false | true | true | ✓ |
| Y15 isSingle === null | true | true | ✓ |
| Y15 primaryDead === false | true | true | ✓ |
| Y15 spouseDead === false | true | true | ✓ |
| Y16 isSingle === null | true | true | ✓ |
| Y16 primaryDead === false | true | true | ✓ |
| Y16 spouseDead === false | true | true | ✓ |
| Y17 isSingle === null | true | true | ✓ |
| Y17 primaryDead === false | true | true | ✓ |
| Y17 spouseDead === false | true | true | ✓ |
| Y18 isSingle === null | true | true | ✓ |
| Y18 primaryDead === false | true | true | ✓ |
| Y18 spouseDead === false | true | true | ✓ |
| Y19 isSingle === null | true | true | ✓ |
| Y19 primaryDead === false | true | true | ✓ |
| Y19 spouseDead === false | true | true | ✓ |
| Y20 isSingle === null | true | true | ✓ |
| Y20 primaryDead === false | true | true | ✓ |
| Y20 spouseDead === false | true | true | ✓ |
| Y21 isSingle === null | true | true | ✓ |
| Y21 primaryDead === false | true | true | ✓ |
| Y21 spouseDead === false | true | true | ✓ |
| Y22 isSingle === null | true | true | ✓ |
| Y22 primaryDead === false | true | true | ✓ |
| Y22 spouseDead === false | true | true | ✓ |
| Y23 isSingle === null | true | true | ✓ |
| Y23 primaryDead === false | true | true | ✓ |
| Y23 spouseDead === false | true | true | ✓ |
| Y24 isSingle === null | true | true | ✓ |
| Y24 primaryDead === false | true | true | ✓ |
| Y24 spouseDead === false | true | true | ✓ |
| Y25 isSingle === null | true | true | ✓ |
| Y25 primaryDead === false | true | true | ✓ |
| Y25 spouseDead === false | true | true | ✓ |
| Y26 isSingle === null | true | true | ✓ |
| Y26 primaryDead === false | true | true | ✓ |
| Y26 spouseDead === false | true | true | ✓ |
| Y27 isSingle === null | true | true | ✓ |
| Y27 primaryDead === false | true | true | ✓ |
| Y27 spouseDead === false | true | true | ✓ |
| Y28 isSingle === null | true | true | ✓ |
| Y28 primaryDead === false | true | true | ✓ |
| Y28 spouseDead === false | true | true | ✓ |
| Y29 isSingle === null | true | true | ✓ |
| Y29 primaryDead === false | true | true | ✓ |
| Y29 spouseDead === false | true | true | ✓ |
| Y30 isSingle === null | true | true | ✓ |
| Y30 primaryDead === false | true | true | ✓ |
| Y30 spouseDead === false | true | true | ✓ |
| Y31 isSingle === null | true | true | ✓ |
| Y31 primaryDead === false | true | true | ✓ |
| Y31 spouseDead === false | true | true | ✓ |
| Y32 isSingle === null | true | true | ✓ |
| Y32 primaryDead === false | true | true | ✓ |
| Y32 spouseDead === false | true | true | ✓ |
| Y0 taxConvergenceFlag === false | true | true | ✓ |
| Y1 taxConvergenceFlag === false | true | true | ✓ |
| Y2 taxConvergenceFlag === false | true | true | ✓ |
| Y3 taxConvergenceFlag === false | true | true | ✓ |
| Y4 taxConvergenceFlag === false | true | true | ✓ |
| Y5 taxConvergenceFlag === false | true | true | ✓ |
| Y6 taxConvergenceFlag === false | true | true | ✓ |
| Y7 taxConvergenceFlag === false | true | true | ✓ |
| Y8 taxConvergenceFlag === false | true | true | ✓ |
| Y9 taxConvergenceFlag === false | true | true | ✓ |
| Y10 taxConvergenceFlag === false | true | true | ✓ |
| Y11 taxConvergenceFlag === false | true | true | ✓ |
| Y12 taxConvergenceFlag === true | true | true | ✓ |
| Y13 taxConvergenceFlag === false | true | true | ✓ |
| Y14 taxConvergenceFlag === false | true | true | ✓ |
| Y15 taxConvergenceFlag === false | true | true | ✓ |
| Y16 taxConvergenceFlag === false | true | true | ✓ |
| Y17 taxConvergenceFlag === false | true | true | ✓ |
| Y18 taxConvergenceFlag === false | true | true | ✓ |
| Y19 taxConvergenceFlag === true | true | true | ✓ |
| Y20 taxConvergenceFlag === false | true | true | ✓ |
| Y21 taxConvergenceFlag === false | true | true | ✓ |
| Y22 taxConvergenceFlag === true | true | true | ✓ |
| Y23 taxConvergenceFlag === false | true | true | ✓ |
| Y24 taxConvergenceFlag === true | true | true | ✓ |
| Y25 taxConvergenceFlag === false | true | true | ✓ |
| Y26 taxConvergenceFlag === false | true | true | ✓ |
| Y27 taxConvergenceFlag === false | true | true | ✓ |
| Y28 taxConvergenceFlag === false | true | true | ✓ |
| Y29 taxConvergenceFlag === false | true | true | ✓ |
| Y30 taxConvergenceFlag === false | true | true | ✓ |
| Y31 taxConvergenceFlag === false | true | true | ✓ |
| Y32 taxConvergenceFlag === false | true | true | ✓ |
| Y0 primaryEarned > 0 | true | true | ✓ |
| Y0 spouseEarned > 0 | true | true | ✓ |
| Y0 earned > 0 | true | true | ✓ |
| Y0 fica > 0 | true | true | ✓ |
| Y1 primaryEarned > 0 | true | true | ✓ |
| Y1 spouseEarned > 0 | true | true | ✓ |
| Y1 earned > 0 | true | true | ✓ |
| Y1 fica > 0 | true | true | ✓ |
| Y2 primaryEarned === 0 | true | true | ✓ |
| Y2 spouseEarned > 0 | true | true | ✓ |
| Y2 earned > 0 | true | true | ✓ |
| Y2 fica > 0 | true | true | ✓ |
| Y3 primaryEarned === 0 | true | true | ✓ |
| Y3 spouseEarned > 0 | true | true | ✓ |
| Y3 earned > 0 | true | true | ✓ |
| Y3 fica > 0 | true | true | ✓ |
| Y4 primaryEarned === 0 | true | true | ✓ |
| Y4 spouseEarned === 0 | true | true | ✓ |
| Y4 earned === 0 | true | true | ✓ |
| Y4 fica === 0 | true | true | ✓ |
| Y5 primaryEarned === 0 | true | true | ✓ |
| Y5 spouseEarned === 0 | true | true | ✓ |
| Y5 earned === 0 | true | true | ✓ |
| Y5 fica === 0 | true | true | ✓ |
| Y6 primaryEarned === 0 | true | true | ✓ |
| Y6 spouseEarned === 0 | true | true | ✓ |
| Y6 earned === 0 | true | true | ✓ |
| Y6 fica === 0 | true | true | ✓ |
| Y7 primaryEarned === 0 | true | true | ✓ |
| Y7 spouseEarned === 0 | true | true | ✓ |
| Y7 earned === 0 | true | true | ✓ |
| Y7 fica === 0 | true | true | ✓ |
| Y8 primaryEarned === 0 | true | true | ✓ |
| Y8 spouseEarned === 0 | true | true | ✓ |
| Y8 earned === 0 | true | true | ✓ |
| Y8 fica === 0 | true | true | ✓ |
| Y9 primaryEarned === 0 | true | true | ✓ |
| Y9 spouseEarned === 0 | true | true | ✓ |
| Y9 earned === 0 | true | true | ✓ |
| Y9 fica === 0 | true | true | ✓ |
| Y10 primaryEarned === 0 | true | true | ✓ |
| Y10 spouseEarned === 0 | true | true | ✓ |
| Y10 earned === 0 | true | true | ✓ |
| Y10 fica === 0 | true | true | ✓ |
| Y11 primaryEarned === 0 | true | true | ✓ |
| Y11 spouseEarned === 0 | true | true | ✓ |
| Y11 earned === 0 | true | true | ✓ |
| Y11 fica === 0 | true | true | ✓ |
| Y12 primaryEarned === 0 | true | true | ✓ |
| Y12 spouseEarned === 0 | true | true | ✓ |
| Y12 earned === 0 | true | true | ✓ |
| Y12 fica === 0 | true | true | ✓ |
| Y13 primaryEarned === 0 | true | true | ✓ |
| Y13 spouseEarned === 0 | true | true | ✓ |
| Y13 earned === 0 | true | true | ✓ |
| Y13 fica === 0 | true | true | ✓ |
| Y14 primaryEarned === 0 | true | true | ✓ |
| Y14 spouseEarned === 0 | true | true | ✓ |
| Y14 earned === 0 | true | true | ✓ |
| Y14 fica === 0 | true | true | ✓ |
| Y15 primaryEarned === 0 | true | true | ✓ |
| Y15 spouseEarned === 0 | true | true | ✓ |
| Y15 earned === 0 | true | true | ✓ |
| Y15 fica === 0 | true | true | ✓ |
| Y16 primaryEarned === 0 | true | true | ✓ |
| Y16 spouseEarned === 0 | true | true | ✓ |
| Y16 earned === 0 | true | true | ✓ |
| Y16 fica === 0 | true | true | ✓ |
| Y17 primaryEarned === 0 | true | true | ✓ |
| Y17 spouseEarned === 0 | true | true | ✓ |
| Y17 earned === 0 | true | true | ✓ |
| Y17 fica === 0 | true | true | ✓ |
| Y18 primaryEarned === 0 | true | true | ✓ |
| Y18 spouseEarned === 0 | true | true | ✓ |
| Y18 earned === 0 | true | true | ✓ |
| Y18 fica === 0 | true | true | ✓ |
| Y19 primaryEarned === 0 | true | true | ✓ |
| Y19 spouseEarned === 0 | true | true | ✓ |
| Y19 earned === 0 | true | true | ✓ |
| Y19 fica === 0 | true | true | ✓ |
| Y20 primaryEarned === 0 | true | true | ✓ |
| Y20 spouseEarned === 0 | true | true | ✓ |
| Y20 earned === 0 | true | true | ✓ |
| Y20 fica === 0 | true | true | ✓ |
| Y21 primaryEarned === 0 | true | true | ✓ |
| Y21 spouseEarned === 0 | true | true | ✓ |
| Y21 earned === 0 | true | true | ✓ |
| Y21 fica === 0 | true | true | ✓ |
| Y22 primaryEarned === 0 | true | true | ✓ |
| Y22 spouseEarned === 0 | true | true | ✓ |
| Y22 earned === 0 | true | true | ✓ |
| Y22 fica === 0 | true | true | ✓ |
| Y23 primaryEarned === 0 | true | true | ✓ |
| Y23 spouseEarned === 0 | true | true | ✓ |
| Y23 earned === 0 | true | true | ✓ |
| Y23 fica === 0 | true | true | ✓ |
| Y24 primaryEarned === 0 | true | true | ✓ |
| Y24 spouseEarned === 0 | true | true | ✓ |
| Y24 earned === 0 | true | true | ✓ |
| Y24 fica === 0 | true | true | ✓ |
| Y25 primaryEarned === 0 | true | true | ✓ |
| Y25 spouseEarned === 0 | true | true | ✓ |
| Y25 earned === 0 | true | true | ✓ |
| Y25 fica === 0 | true | true | ✓ |
| Y26 primaryEarned === 0 | true | true | ✓ |
| Y26 spouseEarned === 0 | true | true | ✓ |
| Y26 earned === 0 | true | true | ✓ |
| Y26 fica === 0 | true | true | ✓ |
| Y27 primaryEarned === 0 | true | true | ✓ |
| Y27 spouseEarned === 0 | true | true | ✓ |
| Y27 earned === 0 | true | true | ✓ |
| Y27 fica === 0 | true | true | ✓ |
| Y28 primaryEarned === 0 | true | true | ✓ |
| Y28 spouseEarned === 0 | true | true | ✓ |
| Y28 earned === 0 | true | true | ✓ |
| Y28 fica === 0 | true | true | ✓ |
| Y29 primaryEarned === 0 | true | true | ✓ |
| Y29 spouseEarned === 0 | true | true | ✓ |
| Y29 earned === 0 | true | true | ✓ |
| Y29 fica === 0 | true | true | ✓ |
| Y30 primaryEarned === 0 | true | true | ✓ |
| Y30 spouseEarned === 0 | true | true | ✓ |
| Y30 earned === 0 | true | true | ✓ |
| Y30 fica === 0 | true | true | ✓ |
| Y31 primaryEarned === 0 | true | true | ✓ |
| Y31 spouseEarned === 0 | true | true | ✓ |
| Y31 earned === 0 | true | true | ✓ |
| Y31 fica === 0 | true | true | ✓ |
| Y32 primaryEarned === 0 | true | true | ✓ |
| Y32 spouseEarned === 0 | true | true | ✓ |
| Y32 earned === 0 | true | true | ✓ |
| Y32 fica === 0 | true | true | ✓ |
| Y0 retired === null | true | true | ✓ |
| Y1 retired === null | true | true | ✓ |
| Y2 retired === true | true | true | ✓ |
| Y3 retired === true | true | true | ✓ |
| Y4 retired === true | true | true | ✓ |
| Y5 retired === true | true | true | ✓ |
| Y6 retired === true | true | true | ✓ |
| Y7 retired === true | true | true | ✓ |
| Y8 retired === true | true | true | ✓ |
| Y9 retired === true | true | true | ✓ |
| Y10 retired === true | true | true | ✓ |
| Y11 retired === true | true | true | ✓ |
| Y12 retired === true | true | true | ✓ |
| Y13 retired === true | true | true | ✓ |
| Y14 retired === true | true | true | ✓ |
| Y15 retired === true | true | true | ✓ |
| Y16 retired === true | true | true | ✓ |
| Y17 retired === true | true | true | ✓ |
| Y18 retired === true | true | true | ✓ |
| Y19 retired === true | true | true | ✓ |
| Y20 retired === true | true | true | ✓ |
| Y21 retired === true | true | true | ✓ |
| Y22 retired === true | true | true | ✓ |
| Y23 retired === true | true | true | ✓ |
| Y24 retired === true | true | true | ✓ |
| Y25 retired === true | true | true | ✓ |
| Y26 retired === true | true | true | ✓ |
| Y27 retired === true | true | true | ✓ |
| Y28 retired === true | true | true | ✓ |
| Y29 retired === true | true | true | ✓ |
| Y30 retired === true | true | true | ✓ |
| Y31 retired === true | true | true | ✓ |
| Y32 retired === true | true | true | ✓ |
| Y0 pension === 0 | true | true | ✓ |
| Y1 pension === 0 | true | true | ✓ |
| Y2 pension === pensionTaxable | true | true | ✓ |
| Y3 pension === pensionTaxable | true | true | ✓ |
| Y4 pension === pensionTaxable | true | true | ✓ |
| Y5 pension === pensionTaxable | true | true | ✓ |
| Y6 pension === pensionTaxable | true | true | ✓ |
| Y7 pension === pensionTaxable | true | true | ✓ |
| Y8 pension === pensionTaxable | true | true | ✓ |
| Y9 pension === pensionTaxable | true | true | ✓ |
| Y10 pension === pensionTaxable | true | true | ✓ |
| Y11 pension === pensionTaxable | true | true | ✓ |
| Y12 pension === pensionTaxable | true | true | ✓ |
| Y13 pension === pensionTaxable | true | true | ✓ |
| Y14 pension === pensionTaxable | true | true | ✓ |
| Y15 pension === pensionTaxable | true | true | ✓ |
| Y16 pension === pensionTaxable | true | true | ✓ |
| Y17 pension === pensionTaxable | true | true | ✓ |
| Y18 pension === pensionTaxable | true | true | ✓ |
| Y19 pension === pensionTaxable | true | true | ✓ |
| Y20 pension === pensionTaxable | true | true | ✓ |
| Y21 pension === pensionTaxable | true | true | ✓ |
| Y22 pension === pensionTaxable | true | true | ✓ |
| Y23 pension === pensionTaxable | true | true | ✓ |
| Y24 pension === pensionTaxable | true | true | ✓ |
| Y25 pension === pensionTaxable | true | true | ✓ |
| Y26 pension === pensionTaxable | true | true | ✓ |
| Y27 pension === pensionTaxable | true | true | ✓ |
| Y28 pension === pensionTaxable | true | true | ✓ |
| Y29 pension === pensionTaxable | true | true | ✓ |
| Y30 pension === pensionTaxable | true | true | ✓ |
| Y31 pension === pensionTaxable | true | true | ✓ |
| Y32 pension === pensionTaxable | true | true | ✓ |
| Y3 pension >= Y2 pension (monotone COLA) | true | true | ✓ |
| Y4 pension >= Y3 pension (monotone COLA) | true | true | ✓ |
| Y5 pension >= Y4 pension (monotone COLA) | true | true | ✓ |
| Y6 pension >= Y5 pension (monotone COLA) | true | true | ✓ |
| Y7 pension >= Y6 pension (monotone COLA) | true | true | ✓ |
| Y8 pension >= Y7 pension (monotone COLA) | true | true | ✓ |
| Y9 pension >= Y8 pension (monotone COLA) | true | true | ✓ |
| Y10 pension >= Y9 pension (monotone COLA) | true | true | ✓ |
| Y11 pension >= Y10 pension (monotone COLA) | true | true | ✓ |
| Y12 pension >= Y11 pension (monotone COLA) | true | true | ✓ |
| Y13 pension >= Y12 pension (monotone COLA) | true | true | ✓ |
| Y14 pension >= Y13 pension (monotone COLA) | true | true | ✓ |
| Y15 pension >= Y14 pension (monotone COLA) | true | true | ✓ |
| Y16 pension >= Y15 pension (monotone COLA) | true | true | ✓ |
| Y17 pension >= Y16 pension (monotone COLA) | true | true | ✓ |
| Y18 pension >= Y17 pension (monotone COLA) | true | true | ✓ |
| Y19 pension >= Y18 pension (monotone COLA) | true | true | ✓ |
| Y20 pension >= Y19 pension (monotone COLA) | true | true | ✓ |
| Y21 pension >= Y20 pension (monotone COLA) | true | true | ✓ |
| Y22 pension >= Y21 pension (monotone COLA) | true | true | ✓ |
| Y23 pension >= Y22 pension (monotone COLA) | true | true | ✓ |
| Y24 pension >= Y23 pension (monotone COLA) | true | true | ✓ |
| Y25 pension >= Y24 pension (monotone COLA) | true | true | ✓ |
| Y26 pension >= Y25 pension (monotone COLA) | true | true | ✓ |
| Y27 pension >= Y26 pension (monotone COLA) | true | true | ✓ |
| Y28 pension >= Y27 pension (monotone COLA) | true | true | ✓ |
| Y29 pension >= Y28 pension (monotone COLA) | true | true | ✓ |
| Y30 pension >= Y29 pension (monotone COLA) | true | true | ✓ |
| Y31 pension >= Y30 pension (monotone COLA) | true | true | ✓ |
| Y32 pension >= Y31 pension (monotone COLA) | true | true | ✓ |
| Y0 ss === 0 | true | true | ✓ |
| Y1 ss === 0 | true | true | ✓ |
| Y2 ss === 0 | true | true | ✓ |
| Y3 ss === 0 | true | true | ✓ |
| Y4 ss === 0 | true | true | ✓ |
| Y5 ss === 0 | true | true | ✓ |
| Y6 ss > 0 | true | true | ✓ |
| Y7 ss > 0 | true | true | ✓ |
| Y8 ss > 0 | true | true | ✓ |
| Y9 ss > 0 | true | true | ✓ |
| Y10 ss > 0 | true | true | ✓ |
| Y11 ss > 0 | true | true | ✓ |
| Y12 ss > 0 | true | true | ✓ |
| Y13 ss > 0 | true | true | ✓ |
| Y14 ss > 0 | true | true | ✓ |
| Y15 ss > 0 | true | true | ✓ |
| Y16 ss > 0 | true | true | ✓ |
| Y17 ss > 0 | true | true | ✓ |
| Y18 ss > 0 | true | true | ✓ |
| Y19 ss > 0 | true | true | ✓ |
| Y20 ss > 0 | true | true | ✓ |
| Y21 ss > 0 | true | true | ✓ |
| Y22 ss > 0 | true | true | ✓ |
| Y23 ss > 0 | true | true | ✓ |
| Y24 ss > 0 | true | true | ✓ |
| Y25 ss > 0 | true | true | ✓ |
| Y26 ss > 0 | true | true | ✓ |
| Y27 ss > 0 | true | true | ✓ |
| Y28 ss > 0 | true | true | ✓ |
| Y29 ss > 0 | true | true | ✓ |
| Y30 ss > 0 | true | true | ✓ |
| Y31 ss > 0 | true | true | ✓ |
| Y32 ss > 0 | true | true | ✓ |
| Y7 ss > Y6 ss (primary onset jump) | true | true | ✓ |
| Y8 ss >= Y7 ss (monotone after both claim) | true | true | ✓ |
| Y9 ss >= Y8 ss (monotone after both claim) | true | true | ✓ |
| Y10 ss >= Y9 ss (monotone after both claim) | true | true | ✓ |
| Y11 ss >= Y10 ss (monotone after both claim) | true | true | ✓ |
| Y12 ss >= Y11 ss (monotone after both claim) | true | true | ✓ |
| Y13 ss >= Y12 ss (monotone after both claim) | true | true | ✓ |
| Y14 ss >= Y13 ss (monotone after both claim) | true | true | ✓ |
| Y15 ss >= Y14 ss (monotone after both claim) | true | true | ✓ |
| Y16 ss >= Y15 ss (monotone after both claim) | true | true | ✓ |
| Y17 ss >= Y16 ss (monotone after both claim) | true | true | ✓ |
| Y18 ss >= Y17 ss (monotone after both claim) | true | true | ✓ |
| Y19 ss >= Y18 ss (monotone after both claim) | true | true | ✓ |
| Y20 ss >= Y19 ss (monotone after both claim) | true | true | ✓ |
| Y21 ss >= Y20 ss (monotone after both claim) | true | true | ✓ |
| Y22 ss >= Y21 ss (monotone after both claim) | true | true | ✓ |
| Y23 ss >= Y22 ss (monotone after both claim) | true | true | ✓ |
| Y24 ss >= Y23 ss (monotone after both claim) | true | true | ✓ |
| Y25 ss >= Y24 ss (monotone after both claim) | true | true | ✓ |
| Y26 ss >= Y25 ss (monotone after both claim) | true | true | ✓ |
| Y27 ss >= Y26 ss (monotone after both claim) | true | true | ✓ |
| Y28 ss >= Y27 ss (monotone after both claim) | true | true | ✓ |
| Y29 ss >= Y28 ss (monotone after both claim) | true | true | ✓ |
| Y30 ss >= Y29 ss (monotone after both claim) | true | true | ✓ |
| Y31 ss >= Y30 ss (monotone after both claim) | true | true | ✓ |
| Y32 ss >= Y31 ss (monotone after both claim) | true | true | ✓ |
| Y0 rmd === 0 | true | true | ✓ |
| Y0 rmdRequired === false | true | true | ✓ |
| Y0 rmdReinvested === 0 | true | true | ✓ |
| Y1 rmd === 0 | true | true | ✓ |
| Y1 rmdRequired === false | true | true | ✓ |
| Y1 rmdReinvested === 0 | true | true | ✓ |
| Y2 rmd === 0 | true | true | ✓ |
| Y2 rmdRequired === false | true | true | ✓ |
| Y2 rmdReinvested === 0 | true | true | ✓ |
| Y3 rmd === 0 | true | true | ✓ |
| Y3 rmdRequired === false | true | true | ✓ |
| Y3 rmdReinvested === 0 | true | true | ✓ |
| Y4 rmd === 0 | true | true | ✓ |
| Y4 rmdRequired === false | true | true | ✓ |
| Y4 rmdReinvested === 0 | true | true | ✓ |
| Y5 rmd === 0 | true | true | ✓ |
| Y5 rmdRequired === false | true | true | ✓ |
| Y5 rmdReinvested === 0 | true | true | ✓ |
| Y6 rmd === 0 | true | true | ✓ |
| Y6 rmdRequired === false | true | true | ✓ |
| Y6 rmdReinvested === 0 | true | true | ✓ |
| Y7 rmd === 0 | true | true | ✓ |
| Y7 rmdRequired === false | true | true | ✓ |
| Y7 rmdReinvested === 0 | true | true | ✓ |
| Y8 rmd === 0 | true | true | ✓ |
| Y8 rmdRequired === false | true | true | ✓ |
| Y8 rmdReinvested === 0 | true | true | ✓ |
| Y9 rmd === 0 | true | true | ✓ |
| Y9 rmdRequired === false | true | true | ✓ |
| Y9 rmdReinvested === 0 | true | true | ✓ |
| Y10 rmd === 0 | true | true | ✓ |
| Y10 rmdRequired === false | true | true | ✓ |
| Y10 rmdReinvested === 0 | true | true | ✓ |
| Y11 rmd === 0 | true | true | ✓ |
| Y11 rmdRequired === false | true | true | ✓ |
| Y11 rmdReinvested === 0 | true | true | ✓ |
| Y12 rmd > 0 | true | true | ✓ |
| Y12 rmdRequired === true | true | true | ✓ |
| Y12 rmdReinvested > 0 | true | true | ✓ |
| Y13 rmd > 0 | true | true | ✓ |
| Y13 rmdRequired === true | true | true | ✓ |
| Y13 rmdReinvested > 0 | true | true | ✓ |
| Y14 rmd > 0 | true | true | ✓ |
| Y14 rmdRequired === true | true | true | ✓ |
| Y14 rmdReinvested > 0 | true | true | ✓ |
| Y15 rmd > 0 | true | true | ✓ |
| Y15 rmdRequired === true | true | true | ✓ |
| Y15 rmdReinvested > 0 | true | true | ✓ |
| Y16 rmd > 0 | true | true | ✓ |
| Y16 rmdRequired === true | true | true | ✓ |
| Y16 rmdReinvested > 0 | true | true | ✓ |
| Y17 rmd > 0 | true | true | ✓ |
| Y17 rmdRequired === true | true | true | ✓ |
| Y17 rmdReinvested > 0 | true | true | ✓ |
| Y18 rmd > 0 | true | true | ✓ |
| Y18 rmdRequired === true | true | true | ✓ |
| Y18 rmdReinvested > 0 | true | true | ✓ |
| Y19 rmd > 0 | true | true | ✓ |
| Y19 rmdRequired === true | true | true | ✓ |
| Y19 rmdReinvested > 0 | true | true | ✓ |
| Y20 rmd > 0 | true | true | ✓ |
| Y20 rmdRequired === true | true | true | ✓ |
| Y20 rmdReinvested > 0 | true | true | ✓ |
| Y21 rmd > 0 | true | true | ✓ |
| Y21 rmdRequired === true | true | true | ✓ |
| Y21 rmdReinvested > 0 | true | true | ✓ |
| Y22 rmd > 0 | true | true | ✓ |
| Y22 rmdRequired === true | true | true | ✓ |
| Y22 rmdReinvested === 0 (LTC final year consumes full RMD) | true | true | ✓ |
| Y23 rmd > 0 | true | true | ✓ |
| Y23 rmdRequired === true | true | true | ✓ |
| Y23 rmdReinvested > 0 | true | true | ✓ |
| Y24 rmd > 0 | true | true | ✓ |
| Y24 rmdRequired === true | true | true | ✓ |
| Y24 rmdReinvested > 0 | true | true | ✓ |
| Y25 rmd > 0 | true | true | ✓ |
| Y25 rmdRequired === true | true | true | ✓ |
| Y25 rmdReinvested > 0 | true | true | ✓ |
| Y26 rmd > 0 | true | true | ✓ |
| Y26 rmdRequired === true | true | true | ✓ |
| Y26 rmdReinvested > 0 | true | true | ✓ |
| Y27 rmd > 0 | true | true | ✓ |
| Y27 rmdRequired === true | true | true | ✓ |
| Y27 rmdReinvested > 0 | true | true | ✓ |
| Y28 rmd > 0 | true | true | ✓ |
| Y28 rmdRequired === true | true | true | ✓ |
| Y28 rmdReinvested > 0 | true | true | ✓ |
| Y29 rmd > 0 | true | true | ✓ |
| Y29 rmdRequired === true | true | true | ✓ |
| Y29 rmdReinvested > 0 | true | true | ✓ |
| Y30 rmd > 0 | true | true | ✓ |
| Y30 rmdRequired === true | true | true | ✓ |
| Y30 rmdReinvested > 0 | true | true | ✓ |
| Y31 rmd > 0 | true | true | ✓ |
| Y31 rmdRequired === true | true | true | ✓ |
| Y31 rmdReinvested > 0 | true | true | ✓ |
| Y32 rmd > 0 | true | true | ✓ |
| Y32 rmdRequired === true | true | true | ✓ |
| Y32 rmdReinvested > 0 | true | true | ✓ |
| Y0 rmdSpouse === 0 | true | true | ✓ |
| Y0 rmdPrimary === rmd | true | true | ✓ |
| Y1 rmdSpouse === 0 | true | true | ✓ |
| Y1 rmdPrimary === rmd | true | true | ✓ |
| Y2 rmdSpouse === 0 | true | true | ✓ |
| Y2 rmdPrimary === rmd | true | true | ✓ |
| Y3 rmdSpouse === 0 | true | true | ✓ |
| Y3 rmdPrimary === rmd | true | true | ✓ |
| Y4 rmdSpouse === 0 | true | true | ✓ |
| Y4 rmdPrimary === rmd | true | true | ✓ |
| Y5 rmdSpouse === 0 | true | true | ✓ |
| Y5 rmdPrimary === rmd | true | true | ✓ |
| Y6 rmdSpouse === 0 | true | true | ✓ |
| Y6 rmdPrimary === rmd | true | true | ✓ |
| Y7 rmdSpouse === 0 | true | true | ✓ |
| Y7 rmdPrimary === rmd | true | true | ✓ |
| Y8 rmdSpouse === 0 | true | true | ✓ |
| Y8 rmdPrimary === rmd | true | true | ✓ |
| Y9 rmdSpouse === 0 | true | true | ✓ |
| Y9 rmdPrimary === rmd | true | true | ✓ |
| Y10 rmdSpouse === 0 | true | true | ✓ |
| Y10 rmdPrimary === rmd | true | true | ✓ |
| Y11 rmdSpouse === 0 | true | true | ✓ |
| Y11 rmdPrimary === rmd | true | true | ✓ |
| Y12 rmdSpouse === 0 | true | true | ✓ |
| Y12 rmdPrimary === rmd | true | true | ✓ |
| Y13 rmdSpouse === 0 | true | true | ✓ |
| Y13 rmdPrimary === rmd | true | true | ✓ |
| Y14 rmdSpouse === 0 | true | true | ✓ |
| Y14 rmdPrimary === rmd | true | true | ✓ |
| Y15 rmdSpouse === 0 | true | true | ✓ |
| Y15 rmdPrimary === rmd | true | true | ✓ |
| Y16 rmdSpouse === 0 | true | true | ✓ |
| Y16 rmdPrimary === rmd | true | true | ✓ |
| Y17 rmdSpouse === 0 | true | true | ✓ |
| Y17 rmdPrimary === rmd | true | true | ✓ |
| Y18 rmdSpouse === 0 | true | true | ✓ |
| Y18 rmdPrimary === rmd | true | true | ✓ |
| Y19 rmdSpouse === 0 | true | true | ✓ |
| Y19 rmdPrimary === rmd | true | true | ✓ |
| Y20 rmdSpouse === 0 | true | true | ✓ |
| Y20 rmdPrimary === rmd | true | true | ✓ |
| Y21 rmdSpouse === 0 | true | true | ✓ |
| Y21 rmdPrimary === rmd | true | true | ✓ |
| Y22 rmdSpouse === 0 | true | true | ✓ |
| Y22 rmdPrimary === rmd | true | true | ✓ |
| Y23 rmdSpouse === 0 | true | true | ✓ |
| Y23 rmdPrimary === rmd | true | true | ✓ |
| Y24 rmdSpouse === 0 | true | true | ✓ |
| Y24 rmdPrimary === rmd | true | true | ✓ |
| Y25 rmdSpouse === 0 | true | true | ✓ |
| Y25 rmdPrimary === rmd | true | true | ✓ |
| Y26 rmdSpouse === 0 | true | true | ✓ |
| Y26 rmdPrimary === rmd | true | true | ✓ |
| Y27 rmdSpouse === 0 | true | true | ✓ |
| Y27 rmdPrimary === rmd | true | true | ✓ |
| Y28 rmdSpouse === 0 | true | true | ✓ |
| Y28 rmdPrimary === rmd | true | true | ✓ |
| Y29 rmdSpouse === 0 | true | true | ✓ |
| Y29 rmdPrimary === rmd | true | true | ✓ |
| Y30 rmdSpouse === 0 | true | true | ✓ |
| Y30 rmdPrimary === rmd | true | true | ✓ |
| Y31 rmdSpouse === 0 | true | true | ✓ |
| Y31 rmdPrimary === rmd | true | true | ✓ |
| Y32 rmdSpouse === 0 | true | true | ✓ |
| Y32 rmdPrimary === rmd | true | true | ✓ |
| Y0 irmaa === 0 | true | true | ✓ |
| Y0 irmaaBracket === 0 | true | true | ✓ |
| Y1 irmaa === 0 | true | true | ✓ |
| Y1 irmaaBracket === 0 | true | true | ✓ |
| Y2 irmaa === 0 | true | true | ✓ |
| Y2 irmaaBracket === 0 | true | true | ✓ |
| Y3 irmaa === 0 | true | true | ✓ |
| Y3 irmaaBracket === 0 | true | true | ✓ |
| Y4 irmaa === 0 | true | true | ✓ |
| Y4 irmaaBracket === 0 | true | true | ✓ |
| Y5 irmaa === 0 | true | true | ✓ |
| Y5 irmaaBracket === 0 | true | true | ✓ |
| Y6 irmaa === 0 | true | true | ✓ |
| Y6 irmaaBracket === 0 | true | true | ✓ |
| Y7 irmaa === 0 | true | true | ✓ |
| Y7 irmaaBracket === 0 | true | true | ✓ |
| Y8 irmaa === 0 | true | true | ✓ |
| Y8 irmaaBracket === 0 | true | true | ✓ |
| Y9 irmaa === 0 | true | true | ✓ |
| Y9 irmaaBracket === 0 | true | true | ✓ |
| Y10 irmaa === 0 | true | true | ✓ |
| Y10 irmaaBracket === 0 | true | true | ✓ |
| Y11 irmaa === 0 | true | true | ✓ |
| Y11 irmaaBracket === 0 | true | true | ✓ |
| Y12 irmaa === 0 | true | true | ✓ |
| Y12 irmaaBracket === 0 | true | true | ✓ |
| Y13 irmaa === 0 | true | true | ✓ |
| Y13 irmaaBracket === 0 | true | true | ✓ |
| Y14 irmaa === 0 | true | true | ✓ |
| Y14 irmaaBracket === 0 | true | true | ✓ |
| Y15 irmaa === 0 | true | true | ✓ |
| Y15 irmaaBracket === 0 | true | true | ✓ |
| Y16 irmaa === 0 | true | true | ✓ |
| Y16 irmaaBracket === 0 | true | true | ✓ |
| Y17 irmaa === 0 | true | true | ✓ |
| Y17 irmaaBracket === 0 | true | true | ✓ |
| Y18 irmaa === 0 | true | true | ✓ |
| Y18 irmaaBracket === 0 | true | true | ✓ |
| Y19 irmaa === 0 | true | true | ✓ |
| Y19 irmaaBracket === 0 | true | true | ✓ |
| Y20 irmaa === 0 | true | true | ✓ |
| Y20 irmaaBracket === 0 | true | true | ✓ |
| Y21 irmaa > 0 | true | true | ✓ |
| Y21 irmaaBracket === 1 | true | true | ✓ |
| Y22 irmaa > 0 | true | true | ✓ |
| Y22 irmaaBracket === 1 | true | true | ✓ |
| Y23 irmaa > 0 | true | true | ✓ |
| Y23 irmaaBracket === 1 | true | true | ✓ |
| Y24 irmaa > 0 | true | true | ✓ |
| Y24 irmaaBracket === 1 | true | true | ✓ |
| Y25 irmaa === 0 | true | true | ✓ |
| Y25 irmaaBracket === 0 | true | true | ✓ |
| Y26 irmaa === 0 | true | true | ✓ |
| Y26 irmaaBracket === 0 | true | true | ✓ |
| Y27 irmaa === 0 | true | true | ✓ |
| Y27 irmaaBracket === 0 | true | true | ✓ |
| Y28 irmaa === 0 | true | true | ✓ |
| Y28 irmaaBracket === 0 | true | true | ✓ |
| Y29 irmaa === 0 | true | true | ✓ |
| Y29 irmaaBracket === 0 | true | true | ✓ |
| Y30 irmaa === 0 | true | true | ✓ |
| Y30 irmaaBracket === 0 | true | true | ✓ |
| Y31 irmaa === 0 | true | true | ✓ |
| Y31 irmaaBracket === 0 | true | true | ✓ |
| Y32 irmaa === 0 | true | true | ✓ |
| Y32 irmaaBracket === 0 | true | true | ✓ |
| Y0 medicareCost === 0 | true | true | ✓ |
| Y1 medicareCost > 0 | true | true | ✓ |
| Y2 medicareCost > 0 | true | true | ✓ |
| Y3 medicareCost > 0 | true | true | ✓ |
| Y4 medicareCost > 0 | true | true | ✓ |
| Y5 medicareCost > 0 | true | true | ✓ |
| Y6 medicareCost > 0 | true | true | ✓ |
| Y7 medicareCost > 0 | true | true | ✓ |
| Y8 medicareCost > 0 | true | true | ✓ |
| Y9 medicareCost > 0 | true | true | ✓ |
| Y10 medicareCost > 0 | true | true | ✓ |
| Y11 medicareCost > 0 | true | true | ✓ |
| Y12 medicareCost > 0 | true | true | ✓ |
| Y13 medicareCost > 0 | true | true | ✓ |
| Y14 medicareCost > 0 | true | true | ✓ |
| Y15 medicareCost > 0 | true | true | ✓ |
| Y16 medicareCost > 0 | true | true | ✓ |
| Y17 medicareCost > 0 | true | true | ✓ |
| Y18 medicareCost > 0 | true | true | ✓ |
| Y19 medicareCost > 0 | true | true | ✓ |
| Y20 medicareCost > 0 | true | true | ✓ |
| Y21 medicareCost > 0 | true | true | ✓ |
| Y22 medicareCost > 0 | true | true | ✓ |
| Y23 medicareCost > 0 | true | true | ✓ |
| Y24 medicareCost > 0 | true | true | ✓ |
| Y25 medicareCost > 0 | true | true | ✓ |
| Y26 medicareCost > 0 | true | true | ✓ |
| Y27 medicareCost > 0 | true | true | ✓ |
| Y28 medicareCost > 0 | true | true | ✓ |
| Y29 medicareCost > 0 | true | true | ✓ |
| Y30 medicareCost > 0 | true | true | ✓ |
| Y31 medicareCost > 0 | true | true | ✓ |
| Y32 medicareCost > 0 | true | true | ✓ |
| Y4 medicareCost >= Y3 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y5 medicareCost >= Y4 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y6 medicareCost >= Y5 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y7 medicareCost >= Y6 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y8 medicareCost >= Y7 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y9 medicareCost >= Y8 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y10 medicareCost >= Y9 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y11 medicareCost >= Y10 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y12 medicareCost >= Y11 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y13 medicareCost >= Y12 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y14 medicareCost >= Y13 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y15 medicareCost >= Y14 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y16 medicareCost >= Y15 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y17 medicareCost >= Y16 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y18 medicareCost >= Y17 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y19 medicareCost >= Y18 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y20 medicareCost >= Y19 (tier 0 stable; both enrolled) | true | true | ✓ |
| Y26 medicareCost >= Y25 (tier 0 post-IRMAA-window) | true | true | ✓ |
| Y27 medicareCost >= Y26 (tier 0 post-IRMAA-window) | true | true | ✓ |
| Y28 medicareCost >= Y27 (tier 0 post-IRMAA-window) | true | true | ✓ |
| Y29 medicareCost >= Y28 (tier 0 post-IRMAA-window) | true | true | ✓ |
| Y30 medicareCost >= Y29 (tier 0 post-IRMAA-window) | true | true | ✓ |
| Y31 medicareCost >= Y30 (tier 0 post-IRMAA-window) | true | true | ✓ |
| Y32 medicareCost >= Y31 (tier 0 post-IRMAA-window) | true | true | ✓ |
| Y0 ltcCost === 0 | true | true | ✓ |
| Y1 ltcCost === 0 | true | true | ✓ |
| Y2 ltcCost === 0 | true | true | ✓ |
| Y3 ltcCost === 0 | true | true | ✓ |
| Y4 ltcCost === 0 | true | true | ✓ |
| Y5 ltcCost === 0 | true | true | ✓ |
| Y6 ltcCost === 0 | true | true | ✓ |
| Y7 ltcCost === 0 | true | true | ✓ |
| Y8 ltcCost === 0 | true | true | ✓ |
| Y9 ltcCost === 0 | true | true | ✓ |
| Y10 ltcCost === 0 | true | true | ✓ |
| Y11 ltcCost === 0 | true | true | ✓ |
| Y12 ltcCost === 0 | true | true | ✓ |
| Y13 ltcCost === 0 | true | true | ✓ |
| Y14 ltcCost === 0 | true | true | ✓ |
| Y15 ltcCost === 0 | true | true | ✓ |
| Y16 ltcCost === 0 | true | true | ✓ |
| Y17 ltcCost === 0 | true | true | ✓ |
| Y18 ltcCost === 0 | true | true | ✓ |
| Y19 ltcCost > 0 | true | true | ✓ |
| Y20 ltcCost > 0 | true | true | ✓ |
| Y21 ltcCost > 0 | true | true | ✓ |
| Y22 ltcCost > 0 | true | true | ✓ |
| Y23 ltcCost === 0 | true | true | ✓ |
| Y24 ltcCost === 0 | true | true | ✓ |
| Y25 ltcCost === 0 | true | true | ✓ |
| Y26 ltcCost === 0 | true | true | ✓ |
| Y27 ltcCost === 0 | true | true | ✓ |
| Y28 ltcCost === 0 | true | true | ✓ |
| Y29 ltcCost === 0 | true | true | ✓ |
| Y30 ltcCost === 0 | true | true | ✓ |
| Y31 ltcCost === 0 | true | true | ✓ |
| Y32 ltcCost === 0 | true | true | ✓ |
| Y0 debtPayments > 0 | true | true | ✓ |
| Y0 debtInterest > 0 | true | true | ✓ |
| Y0 debtPrincipalPaid > 0 | true | true | ✓ |
| Y1 debtPayments > 0 | true | true | ✓ |
| Y1 debtInterest > 0 | true | true | ✓ |
| Y1 debtPrincipalPaid > 0 | true | true | ✓ |
| Y2 debtPayments > 0 | true | true | ✓ |
| Y2 debtInterest > 0 | true | true | ✓ |
| Y2 debtPrincipalPaid > 0 | true | true | ✓ |
| Y3 debtPayments > 0 | true | true | ✓ |
| Y3 debtInterest > 0 | true | true | ✓ |
| Y3 debtPrincipalPaid > 0 | true | true | ✓ |
| Y4 debtPayments > 0 | true | true | ✓ |
| Y4 debtInterest > 0 | true | true | ✓ |
| Y4 debtPrincipalPaid > 0 | true | true | ✓ |
| Y5 debtPayments > 0 | true | true | ✓ |
| Y5 debtInterest > 0 | true | true | ✓ |
| Y5 debtPrincipalPaid > 0 | true | true | ✓ |
| Y6 debtPayments > 0 | true | true | ✓ |
| Y6 debtInterest > 0 | true | true | ✓ |
| Y6 debtPrincipalPaid > 0 | true | true | ✓ |
| Y7 debtPayments > 0 | true | true | ✓ |
| Y7 debtInterest > 0 | true | true | ✓ |
| Y7 debtPrincipalPaid > 0 | true | true | ✓ |
| Y8 debtPayments > 0 | true | true | ✓ |
| Y8 debtInterest > 0 | true | true | ✓ |
| Y8 debtPrincipalPaid > 0 | true | true | ✓ |
| Y9 debtPayments > 0 | true | true | ✓ |
| Y9 debtInterest > 0 | true | true | ✓ |
| Y9 debtPrincipalPaid > 0 | true | true | ✓ |
| Y10 debtPayments > 0 | true | true | ✓ |
| Y10 debtInterest > 0 | true | true | ✓ |
| Y10 debtPrincipalPaid > 0 | true | true | ✓ |
| Y11 debtPayments === 0 | true | true | ✓ |
| Y11 debtInterest === 0 | true | true | ✓ |
| Y11 debtPrincipalPaid === 0 | true | true | ✓ |
| Y12 debtPayments === 0 | true | true | ✓ |
| Y12 debtInterest === 0 | true | true | ✓ |
| Y12 debtPrincipalPaid === 0 | true | true | ✓ |
| Y13 debtPayments === 0 | true | true | ✓ |
| Y13 debtInterest === 0 | true | true | ✓ |
| Y13 debtPrincipalPaid === 0 | true | true | ✓ |
| Y14 debtPayments === 0 | true | true | ✓ |
| Y14 debtInterest === 0 | true | true | ✓ |
| Y14 debtPrincipalPaid === 0 | true | true | ✓ |
| Y15 debtPayments === 0 | true | true | ✓ |
| Y15 debtInterest === 0 | true | true | ✓ |
| Y15 debtPrincipalPaid === 0 | true | true | ✓ |
| Y16 debtPayments === 0 | true | true | ✓ |
| Y16 debtInterest === 0 | true | true | ✓ |
| Y16 debtPrincipalPaid === 0 | true | true | ✓ |
| Y17 debtPayments === 0 | true | true | ✓ |
| Y17 debtInterest === 0 | true | true | ✓ |
| Y17 debtPrincipalPaid === 0 | true | true | ✓ |
| Y18 debtPayments === 0 | true | true | ✓ |
| Y18 debtInterest === 0 | true | true | ✓ |
| Y18 debtPrincipalPaid === 0 | true | true | ✓ |
| Y19 debtPayments === 0 | true | true | ✓ |
| Y19 debtInterest === 0 | true | true | ✓ |
| Y19 debtPrincipalPaid === 0 | true | true | ✓ |
| Y20 debtPayments === 0 | true | true | ✓ |
| Y20 debtInterest === 0 | true | true | ✓ |
| Y20 debtPrincipalPaid === 0 | true | true | ✓ |
| Y21 debtPayments === 0 | true | true | ✓ |
| Y21 debtInterest === 0 | true | true | ✓ |
| Y21 debtPrincipalPaid === 0 | true | true | ✓ |
| Y22 debtPayments === 0 | true | true | ✓ |
| Y22 debtInterest === 0 | true | true | ✓ |
| Y22 debtPrincipalPaid === 0 | true | true | ✓ |
| Y23 debtPayments === 0 | true | true | ✓ |
| Y23 debtInterest === 0 | true | true | ✓ |
| Y23 debtPrincipalPaid === 0 | true | true | ✓ |
| Y24 debtPayments === 0 | true | true | ✓ |
| Y24 debtInterest === 0 | true | true | ✓ |
| Y24 debtPrincipalPaid === 0 | true | true | ✓ |
| Y25 debtPayments === 0 | true | true | ✓ |
| Y25 debtInterest === 0 | true | true | ✓ |
| Y25 debtPrincipalPaid === 0 | true | true | ✓ |
| Y26 debtPayments === 0 | true | true | ✓ |
| Y26 debtInterest === 0 | true | true | ✓ |
| Y26 debtPrincipalPaid === 0 | true | true | ✓ |
| Y27 debtPayments === 0 | true | true | ✓ |
| Y27 debtInterest === 0 | true | true | ✓ |
| Y27 debtPrincipalPaid === 0 | true | true | ✓ |
| Y28 debtPayments === 0 | true | true | ✓ |
| Y28 debtInterest === 0 | true | true | ✓ |
| Y28 debtPrincipalPaid === 0 | true | true | ✓ |
| Y29 debtPayments === 0 | true | true | ✓ |
| Y29 debtInterest === 0 | true | true | ✓ |
| Y29 debtPrincipalPaid === 0 | true | true | ✓ |
| Y30 debtPayments === 0 | true | true | ✓ |
| Y30 debtInterest === 0 | true | true | ✓ |
| Y30 debtPrincipalPaid === 0 | true | true | ✓ |
| Y31 debtPayments === 0 | true | true | ✓ |
| Y31 debtInterest === 0 | true | true | ✓ |
| Y31 debtPrincipalPaid === 0 | true | true | ✓ |
| Y32 debtPayments === 0 | true | true | ✓ |
| Y32 debtInterest === 0 | true | true | ✓ |
| Y32 debtPrincipalPaid === 0 | true | true | ✓ |
| Y0 roth > 0 | true | true | ✓ |
| Y0 preTax > 0 | true | true | ✓ |
| Y0 preTaxSpouse === 0 | true | true | ✓ |
| Y1 roth > 0 | true | true | ✓ |
| Y1 preTax > 0 | true | true | ✓ |
| Y1 preTaxSpouse === 0 | true | true | ✓ |
| Y2 roth > 0 | true | true | ✓ |
| Y2 preTax > 0 | true | true | ✓ |
| Y2 preTaxSpouse === 0 | true | true | ✓ |
| Y3 roth > 0 | true | true | ✓ |
| Y3 preTax > 0 | true | true | ✓ |
| Y3 preTaxSpouse === 0 | true | true | ✓ |
| Y4 roth > 0 | true | true | ✓ |
| Y4 preTax > 0 | true | true | ✓ |
| Y4 preTaxSpouse === 0 | true | true | ✓ |
| Y5 roth > 0 | true | true | ✓ |
| Y5 preTax > 0 | true | true | ✓ |
| Y5 preTaxSpouse === 0 | true | true | ✓ |
| Y6 roth > 0 | true | true | ✓ |
| Y6 preTax > 0 | true | true | ✓ |
| Y6 preTaxSpouse === 0 | true | true | ✓ |
| Y7 roth > 0 | true | true | ✓ |
| Y7 preTax > 0 | true | true | ✓ |
| Y7 preTaxSpouse === 0 | true | true | ✓ |
| Y8 roth > 0 | true | true | ✓ |
| Y8 preTax > 0 | true | true | ✓ |
| Y8 preTaxSpouse === 0 | true | true | ✓ |
| Y9 roth > 0 | true | true | ✓ |
| Y9 preTax > 0 | true | true | ✓ |
| Y9 preTaxSpouse === 0 | true | true | ✓ |
| Y10 roth > 0 | true | true | ✓ |
| Y10 preTax > 0 | true | true | ✓ |
| Y10 preTaxSpouse === 0 | true | true | ✓ |
| Y11 roth > 0 | true | true | ✓ |
| Y11 preTax > 0 | true | true | ✓ |
| Y11 preTaxSpouse === 0 | true | true | ✓ |
| Y12 roth > 0 | true | true | ✓ |
| Y12 preTax > 0 | true | true | ✓ |
| Y12 preTaxSpouse === 0 | true | true | ✓ |
| Y13 roth > 0 | true | true | ✓ |
| Y13 preTax > 0 | true | true | ✓ |
| Y13 preTaxSpouse === 0 | true | true | ✓ |
| Y14 roth > 0 | true | true | ✓ |
| Y14 preTax > 0 | true | true | ✓ |
| Y14 preTaxSpouse === 0 | true | true | ✓ |
| Y15 roth > 0 | true | true | ✓ |
| Y15 preTax > 0 | true | true | ✓ |
| Y15 preTaxSpouse === 0 | true | true | ✓ |
| Y16 roth > 0 | true | true | ✓ |
| Y16 preTax > 0 | true | true | ✓ |
| Y16 preTaxSpouse === 0 | true | true | ✓ |
| Y17 roth > 0 | true | true | ✓ |
| Y17 preTax > 0 | true | true | ✓ |
| Y17 preTaxSpouse === 0 | true | true | ✓ |
| Y18 roth > 0 | true | true | ✓ |
| Y18 preTax > 0 | true | true | ✓ |
| Y18 preTaxSpouse === 0 | true | true | ✓ |
| Y19 roth > 0 | true | true | ✓ |
| Y19 preTax > 0 | true | true | ✓ |
| Y19 preTaxSpouse === 0 | true | true | ✓ |
| Y20 roth > 0 | true | true | ✓ |
| Y20 preTax > 0 | true | true | ✓ |
| Y20 preTaxSpouse === 0 | true | true | ✓ |
| Y21 roth > 0 | true | true | ✓ |
| Y21 preTax > 0 | true | true | ✓ |
| Y21 preTaxSpouse === 0 | true | true | ✓ |
| Y22 roth > 0 | true | true | ✓ |
| Y22 preTax > 0 | true | true | ✓ |
| Y22 preTaxSpouse === 0 | true | true | ✓ |
| Y23 roth > 0 | true | true | ✓ |
| Y23 preTax > 0 | true | true | ✓ |
| Y23 preTaxSpouse === 0 | true | true | ✓ |
| Y24 roth > 0 | true | true | ✓ |
| Y24 preTax > 0 | true | true | ✓ |
| Y24 preTaxSpouse === 0 | true | true | ✓ |
| Y25 roth > 0 | true | true | ✓ |
| Y25 preTax > 0 | true | true | ✓ |
| Y25 preTaxSpouse === 0 | true | true | ✓ |
| Y26 roth > 0 | true | true | ✓ |
| Y26 preTax > 0 | true | true | ✓ |
| Y26 preTaxSpouse === 0 | true | true | ✓ |
| Y27 roth > 0 | true | true | ✓ |
| Y27 preTax > 0 | true | true | ✓ |
| Y27 preTaxSpouse === 0 | true | true | ✓ |
| Y28 roth > 0 | true | true | ✓ |
| Y28 preTax > 0 | true | true | ✓ |
| Y28 preTaxSpouse === 0 | true | true | ✓ |
| Y29 roth > 0 | true | true | ✓ |
| Y29 preTax > 0 | true | true | ✓ |
| Y29 preTaxSpouse === 0 | true | true | ✓ |
| Y30 roth > 0 | true | true | ✓ |
| Y30 preTax > 0 | true | true | ✓ |
| Y30 preTaxSpouse === 0 | true | true | ✓ |
| Y31 roth > 0 | true | true | ✓ |
| Y31 preTax > 0 | true | true | ✓ |
| Y31 preTaxSpouse === 0 | true | true | ✓ |
| Y32 roth > 0 | true | true | ✓ |
| Y32 preTax > 0 | true | true | ✓ |
| Y32 preTaxSpouse === 0 | true | true | ✓ |
| Y0 niitTax === 0 | true | true | ✓ |
| Y1 niitTax === 0 | true | true | ✓ |
| Y2 niitTax === 0 | true | true | ✓ |
| Y3 niitTax === 0 | true | true | ✓ |
| Y4 niitTax === 0 | true | true | ✓ |
| Y5 niitTax === 0 | true | true | ✓ |
| Y6 niitTax === 0 | true | true | ✓ |
| Y7 niitTax === 0 | true | true | ✓ |
| Y8 niitTax === 0 | true | true | ✓ |
| Y9 niitTax === 0 | true | true | ✓ |
| Y10 niitTax === 0 | true | true | ✓ |
| Y11 niitTax === 0 | true | true | ✓ |
| Y12 niitTax === 0 | true | true | ✓ |
| Y13 niitTax === 0 | true | true | ✓ |
| Y14 niitTax === 0 | true | true | ✓ |
| Y15 niitTax === 0 | true | true | ✓ |
| Y16 niitTax === 0 | true | true | ✓ |
| Y23 niitTax === 0 (post-LTC singleton) | true | true | ✓ |
| Y0 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y1 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y2 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y3 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y4 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y5 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y6 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y7 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y8 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y9 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y10 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y11 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y12 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y13 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y14 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y15 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y16 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y17 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y18 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y19 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y20 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y21 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y22 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y23 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y24 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y25 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y26 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y27 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y28 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y29 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y30 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y31 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y32 tax === fedTax + stateTax + fica | true | true | ✓ |
| Y0 ssdi === 0 | true | true | ✓ |
| Y0 withdrawal >= 0 | true | true | ✓ |
| Y0 withdrawalForSpending >= 0 | true | true | ✓ |
| Y1 ssdi === 0 | true | true | ✓ |
| Y1 withdrawal >= 0 | true | true | ✓ |
| Y1 withdrawalForSpending >= 0 | true | true | ✓ |
| Y2 ssdi === 0 | true | true | ✓ |
| Y2 withdrawal >= 0 | true | true | ✓ |
| Y2 withdrawalForSpending >= 0 | true | true | ✓ |
| Y3 ssdi === 0 | true | true | ✓ |
| Y3 withdrawal >= 0 | true | true | ✓ |
| Y3 withdrawalForSpending >= 0 | true | true | ✓ |
| Y4 ssdi === 0 | true | true | ✓ |
| Y4 withdrawal >= 0 | true | true | ✓ |
| Y4 withdrawalForSpending >= 0 | true | true | ✓ |
| Y5 ssdi === 0 | true | true | ✓ |
| Y5 withdrawal >= 0 | true | true | ✓ |
| Y5 withdrawalForSpending >= 0 | true | true | ✓ |
| Y6 ssdi === 0 | true | true | ✓ |
| Y6 withdrawal >= 0 | true | true | ✓ |
| Y6 withdrawalForSpending >= 0 | true | true | ✓ |
| Y7 ssdi === 0 | true | true | ✓ |
| Y7 withdrawal >= 0 | true | true | ✓ |
| Y7 withdrawalForSpending >= 0 | true | true | ✓ |
| Y8 ssdi === 0 | true | true | ✓ |
| Y8 withdrawal >= 0 | true | true | ✓ |
| Y8 withdrawalForSpending >= 0 | true | true | ✓ |
| Y9 ssdi === 0 | true | true | ✓ |
| Y9 withdrawal >= 0 | true | true | ✓ |
| Y9 withdrawalForSpending >= 0 | true | true | ✓ |
| Y10 ssdi === 0 | true | true | ✓ |
| Y10 withdrawal >= 0 | true | true | ✓ |
| Y10 withdrawalForSpending >= 0 | true | true | ✓ |
| Y11 ssdi === 0 | true | true | ✓ |
| Y11 withdrawal >= 0 | true | true | ✓ |
| Y11 withdrawalForSpending >= 0 | true | true | ✓ |
| Y12 ssdi === 0 | true | true | ✓ |
| Y12 withdrawal >= 0 | true | true | ✓ |
| Y12 withdrawalForSpending >= 0 | true | true | ✓ |
| Y13 ssdi === 0 | true | true | ✓ |
| Y13 withdrawal >= 0 | true | true | ✓ |
| Y13 withdrawalForSpending >= 0 | true | true | ✓ |
| Y14 ssdi === 0 | true | true | ✓ |
| Y14 withdrawal >= 0 | true | true | ✓ |
| Y14 withdrawalForSpending >= 0 | true | true | ✓ |
| Y15 ssdi === 0 | true | true | ✓ |
| Y15 withdrawal >= 0 | true | true | ✓ |
| Y15 withdrawalForSpending >= 0 | true | true | ✓ |
| Y16 ssdi === 0 | true | true | ✓ |
| Y16 withdrawal >= 0 | true | true | ✓ |
| Y16 withdrawalForSpending >= 0 | true | true | ✓ |
| Y17 ssdi === 0 | true | true | ✓ |
| Y17 withdrawal >= 0 | true | true | ✓ |
| Y17 withdrawalForSpending >= 0 | true | true | ✓ |
| Y18 ssdi === 0 | true | true | ✓ |
| Y18 withdrawal >= 0 | true | true | ✓ |
| Y18 withdrawalForSpending >= 0 | true | true | ✓ |
| Y19 ssdi === 0 | true | true | ✓ |
| Y19 withdrawal >= 0 | true | true | ✓ |
| Y19 withdrawalForSpending >= 0 | true | true | ✓ |
| Y20 ssdi === 0 | true | true | ✓ |
| Y20 withdrawal >= 0 | true | true | ✓ |
| Y20 withdrawalForSpending >= 0 | true | true | ✓ |
| Y21 ssdi === 0 | true | true | ✓ |
| Y21 withdrawal >= 0 | true | true | ✓ |
| Y21 withdrawalForSpending >= 0 | true | true | ✓ |
| Y22 ssdi === 0 | true | true | ✓ |
| Y22 withdrawal >= 0 | true | true | ✓ |
| Y22 withdrawalForSpending >= 0 | true | true | ✓ |
| Y23 ssdi === 0 | true | true | ✓ |
| Y23 withdrawal >= 0 | true | true | ✓ |
| Y23 withdrawalForSpending >= 0 | true | true | ✓ |
| Y24 ssdi === 0 | true | true | ✓ |
| Y24 withdrawal >= 0 | true | true | ✓ |
| Y24 withdrawalForSpending >= 0 | true | true | ✓ |
| Y25 ssdi === 0 | true | true | ✓ |
| Y25 withdrawal >= 0 | true | true | ✓ |
| Y25 withdrawalForSpending >= 0 | true | true | ✓ |
| Y26 ssdi === 0 | true | true | ✓ |
| Y26 withdrawal >= 0 | true | true | ✓ |
| Y26 withdrawalForSpending >= 0 | true | true | ✓ |
| Y27 ssdi === 0 | true | true | ✓ |
| Y27 withdrawal >= 0 | true | true | ✓ |
| Y27 withdrawalForSpending >= 0 | true | true | ✓ |
| Y28 ssdi === 0 | true | true | ✓ |
| Y28 withdrawal >= 0 | true | true | ✓ |
| Y28 withdrawalForSpending >= 0 | true | true | ✓ |
| Y29 ssdi === 0 | true | true | ✓ |
| Y29 withdrawal >= 0 | true | true | ✓ |
| Y29 withdrawalForSpending >= 0 | true | true | ✓ |
| Y30 ssdi === 0 | true | true | ✓ |
| Y30 withdrawal >= 0 | true | true | ✓ |
| Y30 withdrawalForSpending >= 0 | true | true | ✓ |
| Y31 ssdi === 0 | true | true | ✓ |
| Y31 withdrawal >= 0 | true | true | ✓ |
| Y31 withdrawalForSpending >= 0 | true | true | ✓ |
| Y32 ssdi === 0 | true | true | ✓ |
| Y32 withdrawal >= 0 | true | true | ✓ |
| Y32 withdrawalForSpending >= 0 | true | true | ✓ |
| Test | Expected | Actual | Result |
|---|---|---|---|
| T1 primary age 82 (idx 22) ltcCost | 292,526 | 292,526 | ✓ |
| T1 primary age 85 (idx 25) ltcCost | 338,635 | 338,635 | ✓ |
| T1 primary age 86 (idx 26) ltcCost — window 82-85 closed | 0 | 0 | ✓ |
| T2 spouse-insured, primary age 82 (idx 22) ltcCost — spouse only 77 | 0 | 0 | ✓ |
| T2 spouse-insured, primary age 87 (idx 27) ltcCost — spouse 82 | 373,346 | 373,346 | ✓ |
| T2 spouse-insured, primary age 90 (idx 30) ltcCost — spouse 85 | 432,194 | 432,194 | ✓ |
| T2 spouse-insured, primary age 91 (idx 31) ltcCost — spouse 86, window closed | 0 | 0 | ✓ |
| T3 two streams sum, age 82 (idx 22) ltcCost | 526,547 | 526,547 | ✓ |
| T4 insurance offset, age 82 (idx 20) ltcCost | 228,090 | 228,090 | ✓ |