1pub const STD_DEDUCTION_SINGLE: f64 = 15_000.0;
16pub const STD_DEDUCTION_MARRIED: f64 = 30_000.0;
17
18pub const SS_RATE: f64 = 0.062;
20pub const SS_WAGE_BASE: f64 = 176_100.0;
21pub const MEDICARE_RATE: f64 = 0.0145;
22pub const ADDL_MEDICARE_RATE: f64 = 0.009; #[derive(Clone, Copy, PartialEq, Eq)]
25pub enum FilingStatus { Single, Married }
26
27pub fn standard_deduction(s: FilingStatus) -> f64 {
29 match s { FilingStatus::Single => STD_DEDUCTION_SINGLE, FilingStatus::Married => STD_DEDUCTION_MARRIED }
30}
31
32pub fn brackets(s: FilingStatus) -> &'static [(f64, f64)] {
34 match s {
35 FilingStatus::Single => &[
36 (11_925.0, 0.10), (48_475.0, 0.12), (103_350.0, 0.22), (197_300.0, 0.24),
37 (250_525.0, 0.32), (626_350.0, 0.35), (f64::INFINITY, 0.37),
38 ],
39 FilingStatus::Married => &[
40 (23_850.0, 0.10), (96_950.0, 0.12), (206_700.0, 0.22), (394_600.0, 0.24),
41 (501_050.0, 0.32), (751_600.0, 0.35), (f64::INFINITY, 0.37),
42 ],
43 }
44}
45
46pub fn federal_income_tax(taxable: f64, s: FilingStatus) -> f64 {
48 if taxable <= 0.0 { return 0.0; }
49 let mut tax = 0.0;
50 let mut prev = 0.0;
51 for &(ceil, rate) in brackets(s) {
52 if taxable <= prev { break; }
53 let top = taxable.min(ceil);
54 tax += (top - prev) * rate;
55 prev = ceil;
56 if ceil.is_infinite() { break; }
57 }
58 tax
59}
60
61pub fn fica(gross: f64, s: FilingStatus) -> f64 {
63 let ss = SS_RATE * gross.min(SS_WAGE_BASE);
64 let medicare = MEDICARE_RATE * gross;
65 let threshold = match s { FilingStatus::Single => 200_000.0, FilingStatus::Married => 250_000.0 };
66 let addl = if gross > threshold { ADDL_MEDICARE_RATE * (gross - threshold) } else { 0.0 };
67 ss + medicare + addl
68}
69
70pub fn take_home(gross: f64, s: FilingStatus) -> (f64, f64, f64) {
72 let taxable = (gross - standard_deduction(s)).max(0.0);
73 let fed = federal_income_tax(taxable, s);
74 let f = fica(gross, s);
75 (gross - fed - f, fed, f)
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 #[test]
82 fn bracket_progression_single() {
83 let t = federal_income_tax(60_000.0, FilingStatus::Single);
85 let expect = 0.10*11_925.0 + 0.12*(48_475.0-11_925.0) + 0.22*(60_000.0-48_475.0);
86 assert!((t - expect).abs() < 1e-6);
87 }
88 #[test]
89 fn zero_taxable_zero_tax() {
90 assert_eq!(federal_income_tax(0.0, FilingStatus::Single), 0.0);
91 }
92 #[test]
93 fn fica_caps_ss() {
94 let f = fica(500_000.0, FilingStatus::Single);
96 let expect = 0.062*176_100.0 + 0.0145*500_000.0 + 0.009*(500_000.0-200_000.0);
97 assert!((f - expect).abs() < 1e-6);
98 }
99 #[test]
100 fn take_home_positive() {
101 let (net, fed, fica) = take_home(75_000.0, FilingStatus::Single);
102 assert!(net > 0.0 && fed > 0.0 && fica > 0.0);
103 }
104}