pub const PERSONAL_ALLOWANCE: f64 = 12_570.0; pub const BASIC_LIMIT: f64 = 50_270.0; pub const HIGHER_LIMIT: f64 = 125_140.0; pub const PA_TAPER_START: f64 = 100_000.0;
pub const NI_PRIMARY_THRESHOLD: f64 = 12_570.0;
pub const NI_UPPER_LIMIT: f64 = 50_270.0;
pub const NI_MAIN_RATE: f64 = 0.08; pub const NI_UPPER_RATE: f64 = 0.02;
pub fn personal_allowance(gross: f64) -> f64 {
if gross <= PA_TAPER_START { return PERSONAL_ALLOWANCE; }
let reduced = PERSONAL_ALLOWANCE - (gross - PA_TAPER_START) / 2.0;
reduced.max(0.0)
}
pub fn income_tax(gross: f64) -> f64 {
let pa = personal_allowance(gross);
let taxable = (gross - pa).max(0.0);
if taxable <= 0.0 { return 0.0; }
let basic_width = (BASIC_LIMIT - PERSONAL_ALLOWANCE).max(0.0);
let higher_width = (HIGHER_LIMIT - BASIC_LIMIT).max(0.0);
let mut tax = 0.0;
let mut remaining = taxable;
let b = remaining.min(basic_width); tax += 0.20 * b; remaining -= b;
if remaining > 0.0 { let h = remaining.min(higher_width); tax += 0.40 * h; remaining -= h; }
if remaining > 0.0 { tax += 0.45 * remaining; }
tax
}
pub fn national_insurance(earnings: f64) -> f64 {
if earnings <= NI_PRIMARY_THRESHOLD { return 0.0; }
let main_band = (earnings.min(NI_UPPER_LIMIT) - NI_PRIMARY_THRESHOLD).max(0.0);
let upper = (earnings - NI_UPPER_LIMIT).max(0.0);
NI_MAIN_RATE * main_band + NI_UPPER_RATE * upper
}
pub fn take_home(gross: f64) -> (f64, f64, f64) {
let t = income_tax(gross);
let n = national_insurance(gross);
(gross - t - n, t, n)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_rate_only() {
let t = income_tax(40_000.0);
assert!((t - 0.20 * 27_430.0).abs() < 1e-6);
}
#[test]
fn nil_allowance_at_125140() {
assert!(personal_allowance(125_140.0).abs() < 1e-6);
}
#[test]
fn ni_main_band() {
let n = national_insurance(50_000.0);
assert!((n - 0.08 * (50_000.0 - 12_570.0)).abs() < 1e-6);
}
#[test]
fn ni_upper_rate() {
let n = national_insurance(60_000.0);
let expect = 0.08 * (50_270.0 - 12_570.0) + 0.02 * (60_000.0 - 50_270.0);
assert!((n - expect).abs() < 1e-6);
}
#[test]
fn take_home_decreases_with_tax() {
let (net, _, _) = take_home(50_000.0);
assert!(net < 50_000.0);
}
}