#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Params {
pub cap: f64,
pub phase_out_start: f64,
pub phase_out_rate: f64,
}
impl Params {
pub const SINGLE: Params = Params {
cap: 12_500.0,
phase_out_start: 150_000.0,
phase_out_rate: 0.1, };
pub const JOINT: Params = Params {
cap: 25_000.0,
phase_out_start: 300_000.0,
phase_out_rate: 0.1,
};
#[inline]
pub fn phase_out_end(&self) -> f64 {
self.phase_out_start + self.cap / self.phase_out_rate
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilingStatus {
Single,
Joint,
}
impl FilingStatus {
pub fn default_params(self) -> Params {
match self {
FilingStatus::Single => Params::SINGLE,
FilingStatus::Joint => Params::JOINT,
}
}
}
pub fn effective_cap(magi: f64, params: Params) -> f64 {
if magi <= params.phase_out_start {
params.cap
} else if magi >= params.phase_out_end() {
0.0
} else {
let over = magi - params.phase_out_start;
(params.cap - over * params.phase_out_rate).max(0.0)
}
}
pub fn deduction(qualified_overtime: f64, magi: f64, status: FilingStatus) -> f64 {
deduction_with(qualified_overtime, magi, status.default_params())
}
pub fn deduction_with(qualified_overtime: f64, magi: f64, params: Params) -> f64 {
let ot = qualified_overtime.max(0.0);
let cap = effective_cap(magi, params);
ot.min(cap)
}
pub fn tax_savings(qualified_overtime: f64, magi: f64, marginal_rate: f64, status: FilingStatus) -> f64 {
deduction(qualified_overtime, magi, status) * marginal_rate.max(0.0)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn under_threshold_full_ot() {
let d = deduction(8_000.0, 90_000.0, FilingStatus::Single);
assert!((d - 8_000.0).abs() < 1e-6);
}
#[test]
fn ot_above_cap_capped() {
let d = deduction(40_000.0, 90_000.0, FilingStatus::Single);
assert!((d - 12_500.0).abs() < 1e-6);
}
#[test]
fn joint_cap_is_25k() {
let d = deduction(50_000.0, 200_000.0, FilingStatus::Joint);
assert!((d - 25_000.0).abs() < 1e-6);
}
#[test]
fn phase_out_reduces_linearly() {
let p = Params::SINGLE;
assert!((effective_cap(160_000.0, p) - 11_500.0).abs() < 1e-6);
assert!((effective_cap(170_000.0, p) - 10_500.0).abs() < 1e-6);
}
#[test]
fn phase_out_end_is_zero() {
let p = Params::SINGLE;
assert!((p.phase_out_end() - 275_000.0).abs() < 1e-6);
assert!(effective_cap(275_000.0, p).abs() < 1e-6);
assert_eq!(effective_cap(300_000.0, p), 0.0);
}
#[test]
fn deduction_at_phase_out_is_min_of_ot_and_effective_cap() {
let d = deduction(15_000.0, 160_000.0, FilingStatus::Single);
assert!((d - 11_500.0).abs() < 1e-6);
}
#[test]
fn negative_inputs_clamped() {
assert_eq!(deduction(-1_000.0, 90_000.0, FilingStatus::Single), 0.0);
}
#[test]
fn tax_savings_uses_marginal_rate() {
let s = tax_savings(10_000.0, 80_000.0, 0.22, FilingStatus::Single);
assert!((s - 2_200.0).abs() < 1e-6);
}
#[test]
fn custom_params_override() {
let custom = Params {
cap: 5_000.0,
phase_out_start: 100_000.0,
phase_out_rate: 0.2,
};
assert!((deduction_with(20_000.0, 90_000.0, custom) - 5_000.0).abs() < 1e-6);
}
}