pub const STD_DEDUCTION_SINGLE: f64 = 15_000.0;
pub const STD_DEDUCTION_MARRIED: f64 = 30_000.0;
pub const SS_RATE: f64 = 0.062;
pub const SS_WAGE_BASE: f64 = 176_100.0;
pub const MEDICARE_RATE: f64 = 0.0145;
pub const ADDL_MEDICARE_RATE: f64 = 0.009;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FilingStatus { Single, Married }
pub fn standard_deduction(s: FilingStatus) -> f64 {
match s { FilingStatus::Single => STD_DEDUCTION_SINGLE, FilingStatus::Married => STD_DEDUCTION_MARRIED }
}
pub fn brackets(s: FilingStatus) -> &'static [(f64, f64)] {
match s {
FilingStatus::Single => &[
(11_925.0, 0.10), (48_475.0, 0.12), (103_350.0, 0.22), (197_300.0, 0.24),
(250_525.0, 0.32), (626_350.0, 0.35), (f64::INFINITY, 0.37),
],
FilingStatus::Married => &[
(23_850.0, 0.10), (96_950.0, 0.12), (206_700.0, 0.22), (394_600.0, 0.24),
(501_050.0, 0.32), (751_600.0, 0.35), (f64::INFINITY, 0.37),
],
}
}
pub fn federal_income_tax(taxable: f64, s: FilingStatus) -> f64 {
if taxable <= 0.0 { return 0.0; }
let mut tax = 0.0;
let mut prev = 0.0;
for &(ceil, rate) in brackets(s) {
if taxable <= prev { break; }
let top = taxable.min(ceil);
tax += (top - prev) * rate;
prev = ceil;
if ceil.is_infinite() { break; }
}
tax
}
pub fn fica(gross: f64, s: FilingStatus) -> f64 {
let ss = SS_RATE * gross.min(SS_WAGE_BASE);
let medicare = MEDICARE_RATE * gross;
let threshold = match s { FilingStatus::Single => 200_000.0, FilingStatus::Married => 250_000.0 };
let addl = if gross > threshold { ADDL_MEDICARE_RATE * (gross - threshold) } else { 0.0 };
ss + medicare + addl
}
pub fn take_home(gross: f64, s: FilingStatus) -> (f64, f64, f64) {
let taxable = (gross - standard_deduction(s)).max(0.0);
let fed = federal_income_tax(taxable, s);
let f = fica(gross, s);
(gross - fed - f, fed, f)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bracket_progression_single() {
let t = federal_income_tax(60_000.0, FilingStatus::Single);
let expect = 0.10*11_925.0 + 0.12*(48_475.0-11_925.0) + 0.22*(60_000.0-48_475.0);
assert!((t - expect).abs() < 1e-6);
}
#[test]
fn zero_taxable_zero_tax() {
assert_eq!(federal_income_tax(0.0, FilingStatus::Single), 0.0);
}
#[test]
fn fica_caps_ss() {
let f = fica(500_000.0, FilingStatus::Single);
let expect = 0.062*176_100.0 + 0.0145*500_000.0 + 0.009*(500_000.0-200_000.0);
assert!((f - expect).abs() < 1e-6);
}
#[test]
fn take_home_positive() {
let (net, fed, fica) = take_home(75_000.0, FilingStatus::Single);
assert!(net > 0.0 && fed > 0.0 && fica > 0.0);
}
}