diff --git a/changelog.d/ar-joint-negative-income-tax-table.fixed.md b/changelog.d/ar-joint-negative-income-tax-table.fixed.md new file mode 100644 index 00000000000..14bb3d73217 --- /dev/null +++ b/changelog.d/ar-joint-negative-income-tax-table.fixed.md @@ -0,0 +1 @@ +Require Arkansas married couples to file jointly when either spouse has a negative total income, and compute Arkansas regular income tax from the published tax table bands below the table threshold. diff --git a/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/band_offset.yaml b/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/band_offset.yaml new file mode 100644 index 00000000000..c52883e88a2 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/band_offset.yaml @@ -0,0 +1,42 @@ +description: Arkansas sets the start of each regular income tax table income band this amount above a multiple of the band width. +# From $75,000 the table's rows run "As Much As $75,001 But Less Than $75,101", +# so each band starts $1 above a multiple of $100. The row below them spans +# $74,900 to $75,001, so the $1 offset starts at $75,000. The 2014 and 2015 +# tables end at $50,000, below this row. Negative net taxable income takes the +# zero offset; the callers floor the resulting tax at zero. +metadata: + type: single_amount + threshold_unit: currency-USD + amount_unit: currency-USD + period: year + label: Arkansas regular income tax table band offset + reference: + - title: 2016 AR1000F and AR1000NR Instructions, 2016 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/LongBookwTaxTables_2016.pdf#page=50 + - title: 2017 AR1000F and AR1000NR Instructions, 2017 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/AR1000FandAR1000NRInstructions.pdf#page=50 + - title: 2018 Arkansas Tax Tables, 2018 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2018_Final_TaxTable_with_Cover_Sheet.pdf#page=8 + - title: 2019 AR1000F and AR1000NR Instructions, 2019 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2019_AR1000F_and_AR1000NR_Instructions.pdf#page=31 + - title: 2020 AR1000F and AR1000NR Instructions, 2020 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2020_AR1000F_and_AR1000NR_Instructions.pdf#page=31 + - title: 2021 AR1000F and AR1000NR Instructions, 2021 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2021_AR1000F_and_AR1000NR_Instructions.pdf#page=31 + - title: 2022 AR1000F and AR1000NR Instructions, 2022 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2022_AR1000F_and_AR1000NR_Instructions.pdf#page=34 + - title: 2023 AR1000F and AR1000NR Instructions, 2023 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_and_AR1000NR_Instructions.pdf#page=34 + - title: 2024 AR1000F and AR1000NR Instructions, 2024 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2024_AR1000F_and_AR1000NR_Instructions.pdf#page=33 + - title: 2025 AR1000F and AR1000NR Instructions, 2025 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2025_AR1000F_and_AR1000NR_Instructions.pdf#page=33 +brackets: + - threshold: + 2014-01-01: -.inf + amount: + 2014-01-01: 0 + - threshold: + 2014-01-01: 75_000 + amount: + 2014-01-01: 1 diff --git a/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/band_width.yaml b/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/band_width.yaml new file mode 100644 index 00000000000..d250ef5afad --- /dev/null +++ b/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/band_width.yaml @@ -0,0 +1,32 @@ +description: Arkansas sets each income band of the regular income tax table to this width. +values: + 2014-01-01: 100 +metadata: + unit: currency-USD + period: year + label: Arkansas regular income tax table band width + reference: + - title: 2014 AR1000F and AR1000NR Instructions, 2014 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/LongBookwTaxTables_2014.pdf#page=28 + - title: 2015 Arkansas Tax Tables, 2015 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/TaxTables_2015.pdf#page=4 + - title: 2016 AR1000F and AR1000NR Instructions, 2016 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/LongBookwTaxTables_2016.pdf#page=46 + - title: 2017 AR1000F and AR1000NR Instructions, 2017 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/AR1000FandAR1000NRInstructions.pdf#page=46 + - title: 2018 Arkansas Tax Tables, 2018 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2018_Final_TaxTable_with_Cover_Sheet.pdf#page=4 + - title: 2019 AR1000F and AR1000NR Instructions, 2019 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2019_AR1000F_and_AR1000NR_Instructions.pdf#page=28 + - title: 2020 AR1000F and AR1000NR Instructions, 2020 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2020_AR1000F_and_AR1000NR_Instructions.pdf#page=28 + - title: 2021 AR1000F and AR1000NR Instructions, 2021 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2021_AR1000F_and_AR1000NR_Instructions.pdf#page=28 + - title: 2022 AR1000F and AR1000NR Instructions, 2022 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2022_AR1000F_and_AR1000NR_Instructions.pdf#page=31 + - title: 2023 AR1000F and AR1000NR Instructions, 2023 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_and_AR1000NR_Instructions.pdf#page=31 + - title: 2024 AR1000F and AR1000NR Instructions, 2024 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2024_AR1000F_and_AR1000NR_Instructions.pdf#page=30 + - title: 2025 AR1000F and AR1000NR Instructions, 2025 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2025_AR1000F_and_AR1000NR_Instructions.pdf#page=30 diff --git a/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/threshold.yaml b/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/threshold.yaml new file mode 100644 index 00000000000..5f92e999726 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/threshold.yaml @@ -0,0 +1,62 @@ +description: Arkansas uses the regular income tax table for net taxable income below this amount. +# Each value is the "But Less Than" bound of the table's last row, where the +# table's note starts ("For $100,001 and over, your tax is ..."). +# At and above it, two DFA instructions conflict, and the model follows the +# first: +# - The Indexed Tax Brackets card gives the bracket formula (rate x income +# minus the adjustment) and says a formula "must match the table exactly" +# (2021 card, file p.1; 2024 card, file p.2). The model applies this +# formula to the exact income. +# - The note under each table's last row adds the top rate to the last row's +# tax: "For $100,001 and over, your tax is $3,811 + 3.9% of the excess over +# $100,000" (2024 table, file p.34); "$3,809 + 3.9%" (2025 table, file p.34). +# The note gives less tax than the formula, by the same amount for every +# income at or above the threshold: $3.19 (2014), $3.69 (2015), $3.36 (2016), +# $3.46 (2017), $3.26 (2018), $3.36 (2019), $3.30 (2020), $2.50 (2021), +# $2.80 (2022), $2.30 (2023), $1.60 (2024) and $1.70 (2025). Neither source +# settles which applies above the table, where there are no rows for the +# card's formula to match, so keep the formula unless a DFA source does. +values: + 2014-01-01: 50_000 + 2016-01-01: 86_001 + 2019-01-01: 87_001 + 2020-01-01: 88_001 + 2021-01-01: 91_001 + 2022-01-01: 94_001 + 2023-01-01: 100_001 + # No Regular Income Tax Table has been published for 2026 or later, so the + # model carries this value forward and applies the table method to the + # 2026+ statutory schedules below it. +metadata: + unit: currency-USD + period: year + label: Arkansas regular income tax table threshold + reference: + - title: 2014 AR1000F and AR1000NR Instructions, 2014 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/LongBookwTaxTables_2014.pdf#page=30 + - title: 2015 Arkansas Tax Tables, 2015 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/TaxTables_2015.pdf#page=6 + - title: 2016 AR1000F and AR1000NR Instructions, 2016 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/LongBookwTaxTables_2016.pdf#page=50 + - title: 2017 AR1000F and AR1000NR Instructions, 2017 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/AR1000FandAR1000NRInstructions.pdf#page=50 + - title: 2018 Arkansas Tax Tables, 2018 Regular Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2018_Final_TaxTable_with_Cover_Sheet.pdf#page=8 + - title: 2019 AR1000F and AR1000NR Instructions, 2019 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2019_AR1000F_and_AR1000NR_Instructions.pdf#page=32 + - title: 2020 AR1000F and AR1000NR Instructions, 2020 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2020_AR1000F_and_AR1000NR_Instructions.pdf#page=32 + - title: 2021 AR1000F and AR1000NR Instructions, 2021 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2021_AR1000F_and_AR1000NR_Instructions.pdf#page=32 + - title: 2022 AR1000F and AR1000NR Instructions, 2022 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2022_AR1000F_and_AR1000NR_Instructions.pdf#page=35 + - title: 2023 AR1000F and AR1000NR Instructions, 2023 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_and_AR1000NR_Instructions.pdf#page=35 + - title: 2024 AR1000F and AR1000NR Instructions, 2024 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2024_AR1000F_and_AR1000NR_Instructions.pdf#page=34 + - title: 2025 AR1000F and AR1000NR Instructions, 2025 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2025_AR1000F_and_AR1000NR_Instructions.pdf#page=34 + - title: 2021 Indexed Tax Brackets (a formula must match the table exactly) + href: https://www.dfa.arkansas.gov/wp-content/uploads/TaxBrackets_2021.pdf#page=1 + - title: 2024 Indexed Tax Brackets (a formula must match the table exactly) + href: https://www.dfa.arkansas.gov/wp-content/uploads/2024_TaxBrackets.pdf#page=2 diff --git a/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/whole_dollar_midpoint.yaml b/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/whole_dollar_midpoint.yaml new file mode 100644 index 00000000000..fe559e9664a --- /dev/null +++ b/policyengine_us/parameters/gov/states/ar/tax/income/rates/main/tax_table/whole_dollar_midpoint.yaml @@ -0,0 +1,27 @@ +description: Arkansas uses the midpoint of the whole-dollar incomes in each regular income tax table band if this is true, and the midpoint of the band's bounds otherwise. +# DFA computes each row at the band midpoint and rounds to whole dollars. At +# the midpoint of the bounds ($14,850 for the $14,800-$14,900 row), some rows +# land exactly on 50 cents. Through 2021 the table rounds these up (2021: +# $40,250 x 5.9% - $797.25 = $1,577.50, printed as $1,578). From 2022 it +# rounds them down (2022: $24,750 x 4.9% - $628.25 = $584.50, printed as $584). +# That matches evaluating at the midpoint of the whole-dollar incomes the row +# covers ($24,700 to $24,799: $24,749.50 x 4.9% - $628.25 = $584.48, or $584). +# With these settings every 2014-2025 row matches the published table. +values: + 2014-01-01: false + 2022-01-01: true + # No Regular Income Tax Table has been published for 2026 or later, so the + # model carries this value forward. +metadata: + unit: bool + period: year + label: Arkansas regular income tax table whole-dollar midpoint + reference: + - title: 2021 Indexed Tax Brackets (the calculations in the table are made at the midpoint of each income level) + href: https://www.dfa.arkansas.gov/wp-content/uploads/TaxBrackets_2021.pdf#page=1 + - title: 2021 AR1000F and AR1000NR Instructions, 2021 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2021_AR1000F_and_AR1000NR_Instructions.pdf#page=30 + - title: 2022 AR1000F and AR1000NR Instructions, 2022 Regular Income Tax Table + href: https://www.dfa.arkansas.gov/wp-content/uploads/2022_AR1000F_and_AR1000NR_Instructions.pdf#page=32 + - title: 2024 Indexed Tax Brackets (the calculations in the table are made at the midpoint of each income level) + href: https://www.dfa.arkansas.gov/wp-content/uploads/2024_TaxBrackets.pdf#page=2 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_files_separately.yaml b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_files_separately.yaml index 7243c838026..cafabf4f4c4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_files_separately.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_files_separately.yaml @@ -231,3 +231,219 @@ state_code: AR output: ar_files_separately: false + +# A married couple must file jointly if either spouse had a total negative +# income: "IF ONE SPOUSE HAD A TOTAL NEGATIVE INCOME, YOU MUST FILE MARRIED +# FILING JOINTLY." (2024 AR1000F/AR1000NR instructions, Filing Status 4, p.12: +# https://www.dfa.arkansas.gov/wp-content/uploads/2024_AR1000F_and_AR1000NR_Instructions.pdf#page=12; +# same wording in 2025, p.12). +- name: Separate tax lower, but the spouse's total income is negative, files jointly + period: 2024 + input: + people: + person1: + ar_income_tax_before_non_refundable_credits_indiv: 0 + ar_income_tax_before_non_refundable_credits_joint: 100 + ar_taxable_income_indiv: 50_000 + ar_net_taxable_income_joint: 50_000 + ar_gross_income_indiv: 50_000 + person2: + ar_income_tax_before_non_refundable_credits_indiv: 100 + ar_income_tax_before_non_refundable_credits_joint: 500 + ar_taxable_income_indiv: 0 + ar_net_taxable_income_joint: 0 + ar_gross_income_indiv: -1 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: AR + output: + # Separate tax 100 < joint tax 600, but person2's total income is -1 < 0. + ar_files_separately: false + +- name: Spouse partnership loss of $10 forces a joint return that costs more (issue 9680) + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 27_850 + person2: + age: 38 + partnership_s_corp_income: -10 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: AR + output: + ar_gross_income_indiv: [27_850, -10] + # Separate path: person1 27,850 - 2,410 = 25,440, 2024 Regular Income Tax + # Table row $25,400-$25,500: $584 (p.31); person2 has no taxable income. + ar_income_tax_before_non_refundable_credits_indiv: [584, 0] + # Separate additional credit: 60 each (25,440 and 0 are below 25,800) = 120, + # so the separate path nets 584 - 120 = 464. + # Joint path: AGI 27,850 - 10 = 27,840, joint low income table (0-1 + # dependents) row $27,801-$27,900: $470 (p.29); the joint additional credit + # 120 - ceil((27,840 - 25,800) / 100) x 10 phases out to 0, so it nets 470. + ar_low_income_tax_joint: [470, 0] + # Separate (464) < joint (470), but person2's total income is negative. + ar_files_separately: false + ar_income_tax_before_non_refundable_credits_unit: 470 + # 470 - 2 x 29 personal credits = 412 + ar_non_refundable_credits: 58 + ar_income_tax: 412 + +- name: Head partnership loss of $10 also forces a joint return + period: 2024 + input: + people: + person1: + age: 40 + partnership_s_corp_income: -10 + person2: + age: 38 + employment_income: 27_850 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: AR + output: + ar_gross_income_indiv: [-10, 27_850] + ar_income_tax_before_non_refundable_credits_indiv: [0, 584] + # Same as above with the spouses swapped: joint 470 - 58 = 412. + ar_files_separately: false + ar_income_tax: 412 + +- name: Spouse with zero total income can still file separately (regression) + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 27_850 + person2: + age: 38 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: AR + output: + # Zero is not negative. Separate 584 - 120 = 464 < joint 470 (AGI 27,850, + # row $27,801-$27,900), so the couple files separately. + ar_files_separately: true + ar_income_tax_before_non_refundable_credits_unit: 584 + # 584 - 58 personal credits - 120 additional credits = 406 + ar_income_tax: 406 + +- name: Both spouses with positive income keep the cheaper separate election (regression) + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 40_000 + person2: + age: 38 + employment_income: 40_000 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: AR + output: + # Separate: each 40,000 - 2,410 = 37,590, row $37,500-$37,600: $1,055 (p.31). + ar_income_tax_before_non_refundable_credits_indiv: [1_055, 1_055] + # Joint: 80,000 - 4,820 = 75,180, row $75,101-$75,201: $2,521 (p.33). + ar_income_tax_before_non_refundable_credits_joint: [2_521, 0] + # Separate 2,110 < joint 2,521; neither total income is negative. + ar_files_separately: true + ar_income_tax_before_non_refundable_credits_unit: 2_110 + # 2,110 - 58 = 2,052 + ar_income_tax: 2_052 + +- name: Spouse partnership loss of $300 files jointly, where the joint and separate paths tie (issue 9680) + # The tie already keeps this couple on the joint path (strict <), so this case + # passes without the negative-income rule. The $10 loss cases above isolate + # the rule. + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 28_450 + person2: + age: 38 + partnership_s_corp_income: -300 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: AR + output: + # Joint AGI 28,450 - 300 = 28,150, joint low income table row + # $28,101-$28,200: $501 (p.29). Separately, person1's 26,040 falls in the + # regular row $26,000-$26,100: $606 (p.31), less 45 + 60 of additional + # credits = 501, a tie, which keeps the joint return; the negative total + # income also requires it. + ar_low_income_tax_joint: [501, 0] + ar_income_tax_before_non_refundable_credits_indiv: [606, 0] + ar_files_separately: false + # 501 - 58 = 443 + ar_income_tax: 443 + +- name: Spouse capital loss of $500 files jointly, where the joint path is already cheaper + # The joint path costs less even without the negative-income rule, so this + # case does not isolate the rule. The $10 loss cases above do. + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 27_850 + person2: + age: 38 + short_term_capital_gains: -500 + tax_units: + tax_unit: + members: [person1, person2] + filing_status: JOINT + households: + household: + members: [person1, person2] + state_code: AR + output: + # person2's total income is the -500 capital loss. + ar_gross_income_indiv: [27_850, -500] + # Separate path: person1 27,850 - 2,410 = 25,440, row $25,400-$25,500: + # $584 (p.31), less 60 + 60 of additional credits = 464. + ar_income_tax_before_non_refundable_credits_indiv: [584, 0] + # Joint path: AGI 27,850 - 500 = 27,350, joint low income table row + # $27,301-$27,400: $418 (p.29); the joint additional credit + # 120 - ceil((27,350 - 25,800) / 100) x 10 phases out to 0, so it nets 418. + ar_low_income_tax_joint: [418, 0] + # Joint (418) < separate (464), so the couple files jointly either way. + ar_files_separately: false + # 418 - 58 = 360 + ar_income_tax: 360 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_gross_income_indiv.yaml b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_gross_income_indiv.yaml index 930e680bab8..cc211e3e470 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_gross_income_indiv.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_gross_income_indiv.yaml @@ -129,7 +129,10 @@ output: ar_gross_income_indiv: 60_000 ar_gross_income_joint: 60_000 - ar_income_tax: 1_730.56 + # Net taxable income 60,000 - 2,520 = 57,480; table row $57,400-$57,499, + # midpoint 57,449.50: 609.60 + (57,449.50 - 26,400) x 3.7% = 1,758.43 -> 1,758; + # less the 29 personal credit = 1,729 + ar_income_tax: 1_729 - name: Arkansas unemployment compensation is exempt through 2017 (2016) period: 2016 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_indiv.yaml b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_indiv.yaml index 996e4357caa..b0791e64e0a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_indiv.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_indiv.yaml @@ -25,8 +25,9 @@ ar_taxable_income_indiv: 11_000 state_code: AR output: - # 104 + round((11_000 - 10_301)* 0.03) - ar_income_tax_before_non_refundable_credits_indiv: 125 + # 2022 Regular Income Tax Table (p.31), row $11,000-$11,100: $127. + # Midpoint of $11,000-$11,099 = 11,049.50; 11,049.50 x 3% - 204.97 = 126.52 -> 127 + ar_income_tax_before_non_refundable_credits_indiv: 127 - name: AR income tax rates - taxable income from $14,701 to $24,300 period: 2022 @@ -35,8 +36,9 @@ ar_taxable_income_indiv: 20_000 state_code: AR output: - # 236 + round((20_000 - 14_701)* 0.034) - ar_income_tax_before_non_refundable_credits_indiv: 416 + # 2022 Regular Income Tax Table (p.32), row $20,000-$20,100: $418. + # Midpoint 20,049.50 x 3.4% - 263.77 = 417.91 -> 418 + ar_income_tax_before_non_refundable_credits_indiv: 418 - name: AR income tax rates - taxable income for income over $24,301 period: 2022 @@ -45,8 +47,9 @@ ar_taxable_income_indiv: 25_000 state_code: AR output: - # 562.366 + round((25_000-24_301) * 0.049) = 596 - ar_income_tax_before_non_refundable_credits_indiv: 596 + # 2022 Regular Income Tax Table (p.32), row $25,000-$25,100: $599. + # Midpoint 25,049.50 x 4.9% - 628.25 = 599.18 -> 599 + ar_income_tax_before_non_refundable_credits_indiv: 599 - name: AR income tax rates - taxable income from $15,100 to $25,000 period: 2023 @@ -55,8 +58,9 @@ ar_taxable_income_indiv: 20_000 state_code: AR output: - # 20_000 * 0.034 - 272.37 = 407.63 - ar_income_tax_before_non_refundable_credits_indiv: 407.63 + # 2023 Regular Income Tax Table (p.31), row $20,000-$20,100: $409. + # Midpoint 20,049.50 x 3.4% - 272.37 = 409.31 -> 409 + ar_income_tax_before_non_refundable_credits_indiv: 409 - name: AR income tax rates - taxable income from $25,000 to $89,600 period: 2023 @@ -65,8 +69,9 @@ ar_taxable_income_indiv: 30_000 state_code: AR output: - # 30_000 * 0.047 - 597.35 = 812.65 - ar_income_tax_before_non_refundable_credits_indiv: 812.65 + # 2023 Regular Income Tax Table (p.32), row $30,000-$30,100: $815. + # Midpoint 30,049.50 x 4.7% - 597.35 = 814.98 -> 815 + ar_income_tax_before_non_refundable_credits_indiv: 815 - name: AR income tax rates - taxable income over $100,000 period: 2023 @@ -75,8 +80,10 @@ ar_taxable_income_indiv: 100_000 state_code: AR output: - # 100_000 * 0.047 - 153.7 = 4546.3 - ar_income_tax_before_non_refundable_credits_indiv: 4546.3 + # $100,000 is in the 2023 table's last row, $99,901-$100,001: $4,544 (p.35). + # Midpoint of $99,901-$100,000 = 99,950.50; 99,950.50 x 4.7% - 153.70 = 4,543.97 -> 4,544 + # (the formula at the exact income, 100,000 x 4.7% - 153.70 = 4,546.30, applies from $100,001) + ar_income_tax_before_non_refundable_credits_indiv: 4_544 - name: Case 9, 2025 taxable income in 0% bracket. period: 2025 @@ -95,8 +102,9 @@ ar_taxable_income_indiv: 10_000 state_code: AR output: - # DFA 2025 bracket: 10_000 * 0.02 - 111.98 = 88.02 - ar_income_tax_before_non_refundable_credits_indiv: 88.02 + # 2025 Regular Income Tax Table (p.30), row $10,000-$10,100: $89. + # Midpoint 10,049.50 x 2% - 111.98 = 89.01 -> 89 + ar_income_tax_before_non_refundable_credits_indiv: 89 - name: Case 11, 2025 taxable income in 3.4% bracket. period: 2025 @@ -105,8 +113,9 @@ ar_taxable_income_indiv: 20_000 state_code: AR output: - # DFA 2025 bracket: 20_000 * 0.034 - 287.97 = 392.03 - ar_income_tax_before_non_refundable_credits_indiv: 392.03 + # 2025 Regular Income Tax Table (p.30), row $20,000-$20,100: $394. + # Midpoint 20,049.50 x 3.4% - 287.97 = 393.71 -> 394 + ar_income_tax_before_non_refundable_credits_indiv: 394 - name: Case 12, 2025 taxable income in 3.9% bracket. period: 2025 @@ -115,8 +124,9 @@ ar_taxable_income_indiv: 50_000 state_code: AR output: - # DFA 2025 bracket: 50_000 * 0.039 - 419.96 = 1_530.04 - ar_income_tax_before_non_refundable_credits_indiv: 1_530.04 + # 2025 Regular Income Tax Table (p.32), row $50,000-$50,100: $1,532. + # Midpoint 50,049.50 x 3.9% - 419.96 = 1,531.97 -> 1,532 + ar_income_tax_before_non_refundable_credits_indiv: 1_532 - name: Case 13, 2025 taxable income at $100,000. period: 2025 @@ -125,8 +135,9 @@ ar_taxable_income_indiv: 100_000 state_code: AR output: - # DFA 2025 bracket "$97,801 and over": 100_000 * 0.039 - 89.30 = 3_810.70 - ar_income_tax_before_non_refundable_credits_indiv: 3_810.70 + # $100,000 is in the 2025 table's last row, $99,901-$100,001: $3,809 (p.34). + # Midpoint 99,950.50 x 3.9% - 89.30 ("$97,801 and over" card row) = 3,808.77 -> 3,809 + ar_income_tax_before_non_refundable_credits_indiv: 3_809 - name: Case 14, 2025 taxable income over $100,000. period: 2025 @@ -152,6 +163,9 @@ # 2026 tests follow A.C.A. ยง 26-51-201(a)(4) as amended by HB1001 (2026 1st Extraordinary Session). # Bill schedules: (a)(4)(A) for net income <= $94,700, (a)(4)(B) for > $94,700, plus # the (a)(4)(C) bracket adjustment for $94,701 - $97,600. +# No 2026 Regular Income Tax Table is published yet, so below $100,001 the +# model applies DFA's table method: these schedules at the midpoint of the +# income's $100 table row, rounded to whole dollars. - name: AR HB1001 2026 - 0% bracket, no tax period: 2026 absolute_error_margin: 0.01 @@ -169,8 +183,8 @@ ar_taxable_income_indiv: 10_000 state_code: AR output: - # ($10,000 - $5,600) * 0.02 = $88 - ar_income_tax_before_non_refundable_credits_indiv: 88 + # Row $10,000-$10,099, midpoint 10,049.50: (10,049.50 - 5,600) x 2% = 88.99 -> 89 + ar_income_tax_before_non_refundable_credits_indiv: 89 - name: AR HB1001 2026 - 3% bracket period: 2026 @@ -179,8 +193,9 @@ ar_taxable_income_indiv: 15_000 state_code: AR output: - # $112 + ($15,000 - $11,200) * 0.03 = $112 + $114 = $226 - ar_income_tax_before_non_refundable_credits_indiv: 226 + # Row $15,000-$15,099, midpoint 15,049.50: + # 112 + (15,049.50 - 11,200) x 3% = 112 + 115.485 = 227.485 -> 227 + ar_income_tax_before_non_refundable_credits_indiv: 227 - name: AR HB1001 2026 - 3.4% bracket period: 2026 @@ -189,8 +204,9 @@ ar_taxable_income_indiv: 20_000 state_code: AR output: - # $112 + $144 + ($20,000 - $16,000) * 0.034 = $256 + $136 = $392 - ar_income_tax_before_non_refundable_credits_indiv: 392 + # Row $20,000-$20,099, midpoint 20,049.50: + # 112 + 144 + (20,049.50 - 16,000) x 3.4% = 256 + 137.683 = 393.683 -> 394 + ar_income_tax_before_non_refundable_credits_indiv: 394 - name: AR HB1001 2026 - top low-income bracket (3.7%) period: 2026 @@ -199,8 +215,9 @@ ar_taxable_income_indiv: 50_000 state_code: AR output: - # $609.60 + ($50,000 - $26,400) * 0.037 = $609.60 + $873.20 = $1,482.80 - ar_income_tax_before_non_refundable_credits_indiv: 1_482.80 + # Row $50,000-$50,099, midpoint 50,049.50: + # 609.60 + (50,049.50 - 26,400) x 3.7% = 609.60 + 875.03 = 1,484.63 -> 1,485 + ar_income_tax_before_non_refundable_credits_indiv: 1_485 - name: AR HB1001 2026 - boundary at $94,700 under (a)(4)(A) period: 2026 @@ -209,8 +226,9 @@ ar_taxable_income_indiv: 94_700 state_code: AR output: - # $609.60 + ($94,700 - $26,400) * 0.037 = $609.60 + $2,527.10 = $3,136.70 - ar_income_tax_before_non_refundable_credits_indiv: 3_136.70 + # Row $94,601-$94,700, midpoint 94,650.50 <= 94,700, so (a)(4)(A): + # 609.60 + (94,650.50 - 26,400) x 3.7% = 609.60 + 2,525.27 = 3,134.87 -> 3,135 + ar_income_tax_before_non_refundable_credits_indiv: 3_135 - name: AR HB1001 2026 - bracket adjustment top ($94,701, $290 adj) period: 2026 @@ -219,9 +237,10 @@ ar_taxable_income_indiv: 94_701 state_code: AR output: - # (a)(4)(B): $94 + ($94,701 - $4,700) * 0.037 = $3,424.04 - # (a)(4)(C) adjustment: -$290 -> $3,134.04 - ar_income_tax_before_non_refundable_credits_indiv: 3_134.04 + # Row $94,701-$94,800, midpoint 94,750.50 > 94,700, so (a)(4)(B) less (C): + # 94 + (94,750.50 - 4,700) x 3.7% = 3,425.87; - 290 = 3,135.87 -> 3,136 + # (the table removes the statute's $2.66 drop from $94,700 to $94,701) + ar_income_tax_before_non_refundable_credits_indiv: 3_136 - name: AR HB1001 2026 - bracket adjustment mid ($95,500, $220 adj) period: 2026 @@ -230,9 +249,9 @@ ar_taxable_income_indiv: 95_500 state_code: AR output: - # (a)(4)(B): $94 + ($95,500 - $4,700) * 0.037 = $3,453.60 - # (a)(4)(C) row "$95,401-$95,500" adjustment: -$220 -> $3,233.60 - ar_income_tax_before_non_refundable_credits_indiv: 3_233.60 + # Row $95,401-$95,500, midpoint 95,450.50: + # 94 + (95,450.50 - 4,700) x 3.7% = 3,451.77; (C) row "$95,401-$95,500": - 220 = 3,231.77 -> 3,232 + ar_income_tax_before_non_refundable_credits_indiv: 3_232 - name: AR HB1001 2026 - bracket adjustment mid ($96,500, $120 adj) period: 2026 @@ -241,9 +260,9 @@ ar_taxable_income_indiv: 96_500 state_code: AR output: - # (a)(4)(B): $94 + ($96,500 - $4,700) * 0.037 = $3,490.60 - # (a)(4)(C) row "$96,401-$96,500" adjustment: -$120 -> $3,370.60 - ar_income_tax_before_non_refundable_credits_indiv: 3_370.60 + # Row $96,401-$96,500, midpoint 96,450.50: + # 94 + (96,450.50 - 4,700) x 3.7% = 3,488.77; (C) row "$96,401-$96,500": - 120 = 3,368.77 -> 3,369 + ar_income_tax_before_non_refundable_credits_indiv: 3_369 - name: AR HB1001 2026 - bracket adjustment mid ($97,000, $70 adj) period: 2026 @@ -252,9 +271,9 @@ ar_taxable_income_indiv: 97_000 state_code: AR output: - # (a)(4)(B): $94 + ($97,000 - $4,700) * 0.037 = $3,509.10 - # (a)(4)(C) row "$96,901-$97,000" adjustment: -$70 -> $3,439.10 - ar_income_tax_before_non_refundable_credits_indiv: 3_439.10 + # Row $96,901-$97,000, midpoint 96,950.50: + # 94 + (96,950.50 - 4,700) x 3.7% = 3,507.27; (C) row "$96,901-$97,000": - 70 = 3,437.27 -> 3,437 + ar_income_tax_before_non_refundable_credits_indiv: 3_437 - name: AR HB1001 2026 - bracket adjustment bottom ($97,600, $10 adj) period: 2026 @@ -263,9 +282,9 @@ ar_taxable_income_indiv: 97_600 state_code: AR output: - # (a)(4)(B): $94 + ($97,600 - $4,700) * 0.037 = $3,531.30 - # (a)(4)(C) adjustment: -$10 -> $3,521.30 - ar_income_tax_before_non_refundable_credits_indiv: 3_521.30 + # Row $97,501-$97,600, midpoint 97,550.50: + # 94 + (97,550.50 - 4,700) x 3.7% = 3,529.47; (C) adjustment - 10 = 3,519.47 -> 3,519 + ar_income_tax_before_non_refundable_credits_indiv: 3_519 - name: AR HB1001 2026 - just past adjustment ($97,601, $0 adj) period: 2026 @@ -274,8 +293,9 @@ ar_taxable_income_indiv: 97_601 state_code: AR output: - # (a)(4)(B): $94 + ($97,601 - $4,700) * 0.037 = $3,531.34 - ar_income_tax_before_non_refundable_credits_indiv: 3_531.34 + # Row $97,601-$97,700, midpoint 97,650.50: + # 94 + (97,650.50 - 4,700) x 3.7% = 3,533.17 -> 3,533 + ar_income_tax_before_non_refundable_credits_indiv: 3_533 - name: AR HB1001 2026 - high income (a)(4)(B) period: 2026 @@ -306,8 +326,10 @@ state_code: AR output: # card row "*$90,001 *$90,200 4.70% $543.70" (ranges with * extend an - # extra $100 intentionally): 0.047 x 90,100 - 543.70 = 3,691.00 - ar_income_tax_before_non_refundable_credits_indiv: 3_691 + # extra $100 intentionally). 2023 Regular Income Tax Table (p.35), row + # $90,001-$90,101: $3,689. Midpoint of $90,001-$90,100 = 90,050.50; + # 0.047 x 90,050.50 - 543.70 = 3,688.67 -> 3,689 + ar_income_tax_before_non_refundable_credits_indiv: 3_689 - name: AR 2023 bracket adjustment - formerly duplicated 91,701/91,801 thresholds (pe-us 9026) period: 2023 @@ -322,8 +344,9 @@ ar_income_tax_before_non_refundable_credits_indiv: 3_918.55 # 2025 and 2024 bracket adjustments, keyed to the DFA Indexed Tax Brackets cards -# (2025_TaxBrackets.pdf and 2024_TaxBrackets.pdf, p1-2). Tax = 3.9% x net taxable -# income - the row's minus adjustment. +# (2025_TaxBrackets.pdf and 2024_TaxBrackets.pdf, p1-2). Below $100,001 the tax is +# the Regular Income Tax Table row: 3.9% x the row's midpoint - the minus +# adjustment, rounded to whole dollars. - name: AR 2025 bracket adjustment - top of the $26,400-$94,700 row period: 2025 absolute_error_margin: 0.01 @@ -331,9 +354,10 @@ ar_taxable_income_indiv: 94_700 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$26,400 $94,700 3.90% $419.96": - # 0.039 x 94,700 - 419.96 = 3,273.34 - ar_income_tax_before_non_refundable_credits_indiv: 3_273.34 + # DFA 2025 Indexed Tax Brackets row "$26,400 $94,700 3.90% $419.96". + # 2025 Regular Income Tax Table (p.34), row $94,601-$94,701: $3,271. + # Midpoint 94,650.50 x 3.9% - 419.96 = 3,271.41 -> 3,271 + ar_income_tax_before_non_refundable_credits_indiv: 3_271 - name: AR 2025 bracket adjustment - first $94,701-$94,800 row PE previously skipped period: 2025 @@ -342,10 +366,11 @@ ar_taxable_income_indiv: 94_701 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$94,701 $94,800 3.90% $399.30": - # 0.039 x 94,701 - 399.30 = 3,294.04 - # (PE previously applied the $419.96-row amount, 420, here -> 3,273.34) - ar_income_tax_before_non_refundable_credits_indiv: 3_294.04 + # DFA 2025 Indexed Tax Brackets row "$94,701 $94,800 3.90% $399.30". + # 2025 Regular Income Tax Table (p.34), row $94,701-$94,801: $3,296. + # Midpoint 94,750.50 x 3.9% - 399.30 = 3,295.97 -> 3,296 + # (PE previously applied the $419.96-row amount here) + ar_income_tax_before_non_refundable_credits_indiv: 3_296 - name: AR 2025 bracket adjustment - midpoint of the $94,701-$94,800 row period: 2025 @@ -354,10 +379,8 @@ ar_taxable_income_indiv: 94_751 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$94,701 $94,800 3.90% $399.30": - # 0.039 x 94,751 - 399.30 = 3,295.99 - # (2025 Regular Income Tax Table, $94,701-$94,801: $3,296) - ar_income_tax_before_non_refundable_credits_indiv: 3_295.99 + # Same table row as $94,701: $94,701-$94,801, $3,296 (p.34). + ar_income_tax_before_non_refundable_credits_indiv: 3_296 - name: AR 2025 bracket adjustment - top of the $94,701-$94,800 row period: 2025 @@ -366,9 +389,8 @@ ar_taxable_income_indiv: 94_800 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$94,701 $94,800 3.90% $399.30": - # 0.039 x 94,800 - 399.30 = 3,297.90 - ar_income_tax_before_non_refundable_credits_indiv: 3_297.90 + # $94,800 is still in the table row $94,701-$94,801: $3,296 (p.34). + ar_income_tax_before_non_refundable_credits_indiv: 3_296 - name: AR 2025 bracket adjustment - first income in the $94,801-$94,900 row period: 2025 @@ -377,9 +399,10 @@ ar_taxable_income_indiv: 94_801 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$94,801 $94,900 3.90% $389.30": - # 0.039 x 94,801 - 389.30 = 3,307.94 - ar_income_tax_before_non_refundable_credits_indiv: 3_307.94 + # DFA 2025 Indexed Tax Brackets row "$94,801 $94,900 3.90% $389.30". + # 2025 Regular Income Tax Table (p.34), row $94,801-$94,901: $3,310. + # Midpoint 94,850.50 x 3.9% - 389.30 = 3,309.87 -> 3,310 + ar_income_tax_before_non_refundable_credits_indiv: 3_310 - name: AR 2025 bracket adjustment - midpoint of the $94,801-$94,900 row period: 2025 @@ -388,10 +411,8 @@ ar_taxable_income_indiv: 94_851 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$94,801 $94,900 3.90% $389.30": - # 0.039 x 94,851 - 389.30 = 3,309.89 - # (2025 Regular Income Tax Table, $94,801-$94,901: $3,310) - ar_income_tax_before_non_refundable_credits_indiv: 3_309.89 + # Same table row: $94,801-$94,901, $3,310 (p.34). + ar_income_tax_before_non_refundable_credits_indiv: 3_310 - name: AR 2025 bracket adjustment - first income in the $94,901-$95,000 row period: 2025 @@ -400,9 +421,10 @@ ar_taxable_income_indiv: 94_901 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$94,901 $95,000 3.90% $379.30": - # 0.039 x 94,901 - 379.30 = 3,321.84 - ar_income_tax_before_non_refundable_credits_indiv: 3_321.84 + # DFA 2025 Indexed Tax Brackets row "$94,901 $95,000 3.90% $379.30". + # 2025 Regular Income Tax Table (p.34), row $94,901-$95,001: $3,324. + # Midpoint 94,950.50 x 3.9% - 379.30 = 3,323.77 -> 3,324 + ar_income_tax_before_non_refundable_credits_indiv: 3_324 - name: AR 2025 bracket adjustment - midpoint of the $94,901-$95,000 row period: 2025 @@ -411,10 +433,8 @@ ar_taxable_income_indiv: 94_951 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$94,901 $95,000 3.90% $379.30": - # 0.039 x 94,951 - 379.30 = 3,323.79 - # (2025 Regular Income Tax Table, $94,901-$95,001: $3,324) - ar_income_tax_before_non_refundable_credits_indiv: 3_323.79 + # Same table row: $94,901-$95,001, $3,324 (p.34). + ar_income_tax_before_non_refundable_credits_indiv: 3_324 - name: AR 2025 bracket adjustment - top of the $94,901-$95,000 row period: 2025 @@ -423,9 +443,8 @@ ar_taxable_income_indiv: 95_000 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$94,901 $95,000 3.90% $379.30": - # 0.039 x 95,000 - 379.30 = 3,325.70 - ar_income_tax_before_non_refundable_credits_indiv: 3_325.70 + # $95,000 is still in the table row $94,901-$95,001: $3,324 (p.34). + ar_income_tax_before_non_refundable_credits_indiv: 3_324 - name: AR 2025 bracket adjustment - first income in the $95,001-$95,100 row period: 2025 @@ -434,9 +453,10 @@ ar_taxable_income_indiv: 95_001 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$95,001 $95,100 3.90% $369.30": - # 0.039 x 95,001 - 369.30 = 3,335.74 - ar_income_tax_before_non_refundable_credits_indiv: 3_335.74 + # DFA 2025 Indexed Tax Brackets row "$95,001 $95,100 3.90% $369.30". + # 2025 Regular Income Tax Table (p.34), row $95,001-$95,101: $3,338. + # Midpoint 95,050.50 x 3.9% - 369.30 = 3,337.67 -> 3,338 + ar_income_tax_before_non_refundable_credits_indiv: 3_338 - name: AR 2025 bracket adjustment - top of the last $10-step row period: 2025 @@ -445,9 +465,10 @@ ar_taxable_income_indiv: 97_800 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$97,701 $97,800 3.90% $99.30": - # 0.039 x 97,800 - 99.30 = 3,714.90 - ar_income_tax_before_non_refundable_credits_indiv: 3_714.90 + # DFA 2025 Indexed Tax Brackets row "$97,701 $97,800 3.90% $99.30". + # 2025 Regular Income Tax Table (p.34), row $97,701-$97,801: $3,713. + # Midpoint 97,750.50 x 3.9% - 99.30 = 3,712.97 -> 3,713 + ar_income_tax_before_non_refundable_credits_indiv: 3_713 - name: AR 2025 bracket adjustment - first income in the and-over row period: 2025 @@ -456,9 +477,10 @@ ar_taxable_income_indiv: 97_801 state_code: AR output: - # DFA 2025 Indexed Tax Brackets row "$97,801 and over 3.90% $89.30": - # 0.039 x 97,801 - 89.30 = 3,724.94 - ar_income_tax_before_non_refundable_credits_indiv: 3_724.94 + # DFA 2025 Indexed Tax Brackets row "$97,801 and over 3.90% $89.30". + # 2025 Regular Income Tax Table (p.34), row $97,801-$97,901: $3,727. + # Midpoint 97,850.50 x 3.9% - 89.30 = 3,726.87 -> 3,727 + ar_income_tax_before_non_refundable_credits_indiv: 3_727 - name: AR 2024 bracket adjustment - inside the asterisked $94,301-$94,500 row period: 2024 @@ -467,10 +489,11 @@ ar_taxable_income_indiv: 94_450 state_code: AR output: - # DFA 2024 Indexed Tax Brackets row "*$94,301 *$94,500 3.90% $197.40" (asterisked, $200 wide): - # 0.039 x 94,450 - 197.40 = 3,486.15 - # (2024 Regular Income Tax Table, $94,401-$94,501: $3,486; PE previously applied $187.40 here -> 3,496.15) - ar_income_tax_before_non_refundable_credits_indiv: 3_486.15 + # DFA 2024 Indexed Tax Brackets row "*$94,301 *$94,500 3.90% $197.40" (asterisked, $200 wide). + # 2024 Regular Income Tax Table (p.34), row $94,401-$94,501: $3,486. + # Midpoint 94,450.50 x 3.9% - 197.40 = 3,486.17 -> 3,486 + # (PE previously applied $187.40 here -> 3,496.15) + ar_income_tax_before_non_refundable_credits_indiv: 3_486 - name: AR 2024 bracket adjustment - top of the asterisked $94,301-$94,500 row period: 2024 @@ -479,9 +502,8 @@ ar_taxable_income_indiv: 94_500 state_code: AR output: - # DFA 2024 Indexed Tax Brackets row "*$94,301 *$94,500 3.90% $197.40": - # 0.039 x 94,500 - 197.40 = 3,488.10 - ar_income_tax_before_non_refundable_credits_indiv: 3_488.10 + # $94,500 is still in the table row $94,401-$94,501: $3,486 (p.34). + ar_income_tax_before_non_refundable_credits_indiv: 3_486 - name: AR 2024 bracket adjustment - first income after the asterisked row period: 2024 @@ -490,9 +512,10 @@ ar_taxable_income_indiv: 94_501 state_code: AR output: - # DFA 2024 Indexed Tax Brackets row "$94,501 $94,600 3.90% $187.40": - # 0.039 x 94,501 - 187.40 = 3,498.14 - ar_income_tax_before_non_refundable_credits_indiv: 3_498.14 + # DFA 2024 Indexed Tax Brackets row "$94,501 $94,600 3.90% $187.40". + # 2024 Regular Income Tax Table (p.34), row $94,501-$94,601: $3,500. + # Midpoint 94,550.50 x 3.9% - 187.40 = 3,500.07 -> 3,500 + ar_income_tax_before_non_refundable_credits_indiv: 3_500 - name: AR 2024 bracket adjustment - top of the last $10-step row period: 2024 @@ -501,9 +524,10 @@ ar_taxable_income_indiv: 95_500 state_code: AR output: - # DFA 2024 Indexed Tax Brackets row "$95,401 $95,500 3.90% $97.40": - # 0.039 x 95,500 - 97.40 = 3,627.10 - ar_income_tax_before_non_refundable_credits_indiv: 3_627.10 + # DFA 2024 Indexed Tax Brackets row "$95,401 $95,500 3.90% $97.40". + # 2024 Regular Income Tax Table (p.34), row $95,401-$95,501: $3,625. + # Midpoint 95,450.50 x 3.9% - 97.40 = 3,625.17 -> 3,625 + ar_income_tax_before_non_refundable_credits_indiv: 3_625 - name: AR 2024 bracket adjustment - first income in the and-over row period: 2024 @@ -512,9 +536,10 @@ ar_taxable_income_indiv: 95_501 state_code: AR output: - # DFA 2024 Indexed Tax Brackets row "$95,501 and over 3.90% $87.40": - # 0.039 x 95,501 - 87.40 = 3,637.14 - ar_income_tax_before_non_refundable_credits_indiv: 3_637.14 + # DFA 2024 Indexed Tax Brackets row "$95,501 and over 3.90% $87.40". + # 2024 Regular Income Tax Table (p.34), row $95,501-$95,601: $3,639. + # Midpoint 95,550.50 x 3.9% - 87.40 = 3,639.07 -> 3,639 + ar_income_tax_before_non_refundable_credits_indiv: 3_639 # 2027 cases use incomes whose tax survives a one-step change in the projected # uprating factor. The (B)-(C) rows near the indexed threshold (about $97,500) @@ -537,8 +562,8 @@ ar_taxable_income_indiv: 5_900 state_code: AR output: - # (5,900 - 5,800) x 2% = 2.00 - ar_income_tax_before_non_refundable_credits_indiv: 2 + # Row $5,900-$5,999, midpoint 5,949.50: (5,949.50 - 5,800) x 2% = 2.99 -> 3 + ar_income_tax_before_non_refundable_credits_indiv: 3 - name: AR 2027 indexed Act 1 of 2026 tables - income the frozen 2026 reduction undertaxed by $108 period: 2027 @@ -547,8 +572,10 @@ ar_taxable_income_indiv: 11_300 state_code: AR output: - # (11,300 - 5,800) x 2% = 110.00; the frozen 2026 reduction schedule gave 2% x 11,300 - 224 = 2.00 - ar_income_tax_before_non_refundable_credits_indiv: 110 + # Row $11,300-$11,399, midpoint 11,349.50: (11,349.50 - 5,800) x 2% = 110.99 -> 111; + # at the exact income, (11,300 - 5,800) x 2% = 110.00, and the frozen 2026 + # reduction schedule gave 2% x 11,300 - 224 = 2.00 + ar_income_tax_before_non_refundable_credits_indiv: 111 - name: AR 2027 indexed Act 1 of 2026 tables - start of the 3% row period: 2027 @@ -557,8 +584,9 @@ ar_taxable_income_indiv: 11_500 state_code: AR output: - # (11,500 - 5,800) x 2% = 114.00 - ar_income_tax_before_non_refundable_credits_indiv: 114 + # Row $11,500-$11,599, midpoint 11,549.50: + # (11,500 - 5,800) x 2% + (11,549.50 - 11,500) x 3% = 114 + 1.485 = 115.485 -> 115 + ar_income_tax_before_non_refundable_credits_indiv: 115 - name: AR 2027 indexed Act 1 of 2026 tables - start of the 3.4% row period: 2027 @@ -567,8 +595,9 @@ ar_taxable_income_indiv: 16_500 state_code: AR output: - # 114 + (16,500 - 11,500) x 3% = 264.00 - ar_income_tax_before_non_refundable_credits_indiv: 264 + # Row $16,500-$16,599, midpoint 16,549.50: + # 114 + (16,500 - 11,500) x 3% + (16,549.50 - 16,500) x 3.4% = 264 + 1.683 = 265.683 -> 266 + ar_income_tax_before_non_refundable_credits_indiv: 266 - name: AR 2027 indexed Act 1 of 2026 tables - income the frozen 2026 reduction undertaxed by $70.20 period: 2027 @@ -577,8 +606,10 @@ ar_taxable_income_indiv: 26_500 state_code: AR output: - # 264 + (26,500 - 16,500) x 3.4% = 604.00; the frozen 2026 reduction schedule gave 3.4% x 26,500 - 367.20 = 533.80 - ar_income_tax_before_non_refundable_credits_indiv: 604 + # Row $26,500-$26,599, midpoint 26,549.50: + # 264 + (26,549.50 - 16,500) x 3.4% = 264 + 341.683 = 605.683 -> 606; + # the frozen 2026 reduction schedule gave 3.4% x 26,500 - 367.20 = 533.80 + ar_income_tax_before_non_refundable_credits_indiv: 606 - name: AR 2027 indexed Act 1 of 2026 tables - start of the 3.7% row period: 2027 @@ -587,8 +618,9 @@ ar_taxable_income_indiv: 27_200 state_code: AR output: - # 264 + (27,200 - 16,500) x 3.4% = 627.80 - ar_income_tax_before_non_refundable_credits_indiv: 627.80 + # Row $27,200-$27,299, midpoint 27,249.50: + # 264 + (27,200 - 16,500) x 3.4% + (27,249.50 - 27,200) x 3.7% = 627.80 + 1.8315 = 629.63 -> 630 + ar_income_tax_before_non_refundable_credits_indiv: 630 - name: AR 2027 indexed Act 1 of 2026 tables - top of table (A) at the indexed high-income threshold period: 2027 @@ -597,8 +629,10 @@ ar_taxable_income_indiv: 97_500 state_code: AR output: - # 627.80 + (97,500 - 27,200) x 3.7% = 3,228.90; the threshold is 94,700 x 1.0298 = 97,522, nearest $100 - ar_income_tax_before_non_refundable_credits_indiv: 3_228.90 + # The threshold is 94,700 x 1.0298 = 97,522, nearest $100 = 97,500. Row + # $97,401-$97,500, midpoint 97,450.50 <= 97,500, so table (A): + # 627.80 + (97,450.50 - 27,200) x 3.7% = 627.80 + 2,599.27 = 3,227.07 -> 3,227 + ar_income_tax_before_non_refundable_credits_indiv: 3_227 - name: AR 2027 indexed Act 1 of 2026 tables - high income period: 2027 @@ -609,3 +643,157 @@ output: # 96 + (150,000 - 4,800) x 3.7% = 5,468.40 ar_income_tax_before_non_refundable_credits_indiv: 5_468.40 + +# Regular Income Tax Table (issue 9681). Below the table threshold ($100,001 +# from 2023), filers look up net taxable income in $100 rows. DFA computes each +# row at its midpoint and rounds to whole dollars: "If you use a formula to +# calculate Arkansas income tax, the results must match the table exactly. The +# calculations in the table are made at the midpoint of each income level." +# (2024 Indexed Tax Brackets, https://www.dfa.arkansas.gov/wp-content/uploads/2024_TaxBrackets.pdf#page=2). +# Pages below are file pages of that year's AR1000F/AR1000NR instructions. +- name: AR regular tax table 2024 - income inside the $35,100-$35,200 row + period: 2024 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 35_180 + state_code: AR + output: + # 2024 Regular Income Tax Table (p.31), row $35,100-$35,200: $961. + # Midpoint of $35,100-$35,199 = 35,149.50; 35,149.50 x 3.9% - 409.86 = 960.97 -> 961 + # (the bracket formula at the exact income gives 35,180 x 3.9% - 409.86 = 962.16) + ar_income_tax_before_non_refundable_credits_indiv: 961 + +- name: AR regular tax table 2024 - first income of the $35,100-$35,200 row + period: 2024 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 35_100 + state_code: AR + output: + # "As Much As" $35,100: same row, $961 (p.31). + ar_income_tax_before_non_refundable_credits_indiv: 961 + +- name: AR regular tax table 2024 - just below the $35,100-$35,200 row + period: 2024 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 35_099.99 + state_code: AR + output: + # "But Less Than" $35,100: row $35,000-$35,100, $957 (p.31). + # Midpoint 35,049.50 x 3.9% - 409.86 = 957.07 -> 957 + ar_income_tax_before_non_refundable_credits_indiv: 957 + +- name: AR regular tax table 2024 - round $80,000 falls in the $79,901-$80,001 row + period: 2024 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 80_000 + state_code: AR + output: + # From $75,000 rows start $1 above the hundred. Row $79,901-$80,001: $2,708 (p.33). + # Midpoint of $79,901-$80,000 = 79,950.50; 79,950.50 x 3.9% - 409.86 = 2,708.21 -> 2,708 + ar_income_tax_before_non_refundable_credits_indiv: 2_708 + +- name: AR regular tax table 2024 - $80,001 starts the next row + period: 2024 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 80_001 + state_code: AR + output: + # Row $80,001-$80,101: $2,712 (p.33). + # Midpoint 80,050.50 x 3.9% - 409.86 = 2,712.11 -> 2,712 + ar_income_tax_before_non_refundable_credits_indiv: 2_712 + +- name: AR regular tax table 2024 - $75,000 is in the $101-wide $74,900-$75,001 row + period: 2024 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 75_000 + state_code: AR + output: + # Row $74,900-$75,001: $2,513 (p.33). + # Midpoint of $74,901-$75,000 = 74,950.50; 74,950.50 x 3.9% - 409.86 = 2,513.21 -> 2,513 + ar_income_tax_before_non_refundable_credits_indiv: 2_513 + +- name: AR regular tax table 2024 - $100,000 is in the last table row + period: 2024 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 100_000 + state_code: AR + output: + # Row $99,901-$100,001: $3,811 (p.34). + # Midpoint 99,950.50 x 3.9% - 87.40 = 3,810.67 -> 3,811 + ar_income_tax_before_non_refundable_credits_indiv: 3_811 + +- name: AR regular tax table 2024 - the formula applies from $100,001 + period: 2024 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 100_001 + state_code: AR + output: + # "For $100,001 and over" (p.34) the model keeps the bracket formula at the + # exact income: 100,001 x 3.9% - 87.40 = 3,812.64. The table's note, + # "$3,811 + 3.9% of the excess over $100,000", would give $1.60 less + # (threshold.yaml documents the choice). + ar_income_tax_before_non_refundable_credits_indiv: 3_812.64 + +- name: AR regular tax table 2025 - income inside the $35,100-$35,200 row + period: 2025 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 35_180 + state_code: AR + output: + # 2025 Regular Income Tax Table (p.31), row $35,100-$35,200: $951. + # Midpoint 35,149.50 x 3.9% - 419.96 = 950.87 -> 951 + ar_income_tax_before_non_refundable_credits_indiv: 951 + +- name: AR regular tax table 2021 - a 50-cent midpoint rounds up + period: 2021 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 40_250 + state_code: AR + output: + # 2021 Regular Income Tax Table (p.30), row $40,200-$40,300: $1,578. + # Through 2021 the midpoint is that of the row's bounds: + # (40,200 + 40,300) / 2 = 40,250; 40,250 x 5.9% - 797.25 = 1,577.50 -> 1,578 + ar_income_tax_before_non_refundable_credits_indiv: 1_578 + +- name: AR regular tax table 2022 - the matching 50-cent row rounds down + period: 2022 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 24_750 + state_code: AR + output: + # 2022 Regular Income Tax Table (p.32), row $24,700-$24,800: $584. + # From 2022 the midpoint is that of the whole-dollar incomes in the row: + # (24,700 + 24,799) / 2 = 24,749.50; 24,749.50 x 4.9% - 628.25 = 584.48 -> 584 + # (the bounds' midpoint would give 24,750 x 4.9% - 628.25 = 584.50) + ar_income_tax_before_non_refundable_credits_indiv: 584 + +- name: AR regular tax table 2015 - last row below the $50,000 threshold + period: 2015 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 49_999 + state_code: AR + output: + # 2015 Tax Tables (TaxTables_2015.pdf p.6), row $49,900-$50,000: $2,547. + # Midpoint 49,950 x 7% - 949.24 = 2,547.26 -> 2,547 + ar_income_tax_before_non_refundable_credits_indiv: 2_547 + +- name: AR regular tax table 2015 - the formula applies from $50,000 + period: 2015 + absolute_error_margin: 0.01 + input: + ar_taxable_income_indiv: 50_000 + state_code: AR + output: + # "For $50,000 and over" (p.6): 50,000 x 7% - 949.24 = 2,550.76 + ar_income_tax_before_non_refundable_credits_indiv: 2_550.76 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_joint.yaml b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_joint.yaml index 3429a1c3b37..973c835dbc3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_joint.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_joint.yaml @@ -28,8 +28,9 @@ ar_low_income_tax_joint: 500 state_code: AR output: - # 104 + round((11_000 - 10_301)* 0.03) - ar_income_tax_before_non_refundable_credits_joint: 125 + # 2022 Regular Income Tax Table (p.31), row $11,000-$11,100: $127 < 500. + # Midpoint of $11,000-$11,099 = 11,049.50; 11,049.50 x 3% - 204.97 = 126.52 -> 127 + ar_income_tax_before_non_refundable_credits_joint: 127 - name: AR income tax rates - taxable income from $14,701 to $24,300 period: 2022 @@ -39,8 +40,9 @@ ar_low_income_tax_joint: 500 state_code: AR output: - # 236 + round((20_000 - 14_701)* 0.034) - ar_income_tax_before_non_refundable_credits_joint: 416 + # 2022 Regular Income Tax Table (p.32), row $20,000-$20,100: $418 < 500. + # Midpoint 20,049.50 x 3.4% - 263.77 = 417.91 -> 418 + ar_income_tax_before_non_refundable_credits_joint: 418 - name: AR income tax rates - taxable income for income over $24,301 period: 2022 @@ -50,8 +52,9 @@ ar_low_income_tax_joint: 1_000 state_code: AR output: - # 562.366 + round((25_000-24_301) * 0.049) = 596 - ar_income_tax_before_non_refundable_credits_joint: 596 + # 2022 Regular Income Tax Table (p.32), row $25,000-$25,100: $599 < 1,000. + # Midpoint 25,049.50 x 4.9% - 628.25 = 599.18 -> 599 + ar_income_tax_before_non_refundable_credits_joint: 599 - name: AR income tax rates - taxable income for income over $25,000 period: 2023 @@ -61,7 +64,9 @@ ar_low_income_tax_joint: 1_000 state_code: AR output: - ar_income_tax_before_non_refundable_credits_joint: 591 + # 2023 Regular Income Tax Table (p.32), row $25,300-$25,400: $594 < 1,000. + # Midpoint 25,349.50 x 4.7% - 597.35 = 594.08 -> 594 + ar_income_tax_before_non_refundable_credits_joint: 594 - name: AR income tax rates - taxable income from $15,100 to $25,000 period: 2023 @@ -71,8 +76,9 @@ ar_low_income_tax_joint: 1_000 state_code: AR output: - # 20_000 * 0.034 - 272.37 = 407.63 < 1_000 - ar_income_tax_before_non_refundable_credits_joint: 407.63 + # 2023 Regular Income Tax Table (p.31), row $20,000-$20,100: $409 < 1,000. + # Midpoint 20,049.50 x 3.4% - 272.37 = 409.31 -> 409 + ar_income_tax_before_non_refundable_credits_joint: 409 - name: AR income tax rates - taxable income from $25,000 to $89,600 period: 2023 @@ -82,8 +88,9 @@ ar_low_income_tax_joint: 1_000 state_code: AR output: - # 30_000 * 0.047 - 597.35 = 812.65 < 1_000 - ar_income_tax_before_non_refundable_credits_joint: 812.65 + # 2023 Regular Income Tax Table (p.32), row $30,000-$30,100: $815 < 1,000. + # Midpoint 30,049.50 x 4.7% - 597.35 = 814.98 -> 815 + ar_income_tax_before_non_refundable_credits_joint: 815 - name: AR income tax rates - taxable income over $100,000 period: 2023 @@ -117,10 +124,11 @@ ar_low_income_tax_joint: 5_000 state_code: AR output: - # Regular: 50_000 * 0.039 - 419.96 = 1_530.04 + # Regular: 2025 Regular Income Tax Table (p.32), row $50,000-$50,100: $1,532 + # (midpoint 50,049.50 x 3.9% - 419.96 = 1,531.97 -> 1,532) # Low income cap: 5_000 - # min(1_530.04, 5_000) = 1_530.04 - ar_income_tax_before_non_refundable_credits_joint: 1_530.04 + # min(1_532, 5_000) = 1_532 + ar_income_tax_before_non_refundable_credits_joint: 1_532 - name: AR 2021 MFJ with two dependents just above the low-income table cutoff uses the regular table (pe-us 9024) period: 2021 @@ -208,6 +216,8 @@ ar_low_income_tax_joint: 5_000 state_code: AR output: - # Indexed 2027 rows start at 5,800 / 11,500 / 16,500 / 27,200 (nearest $100): - # 5,700 x 2% + 5,000 x 3% + 10,700 x 3.4% + (50,000 - 27,200) x 3.7% = 1,471.40 - ar_income_tax_before_non_refundable_credits_joint: 1_471.40 + # Indexed 2027 rows start at 5,800 / 11,500 / 16,500 / 27,200 (nearest $100). + # Table row $50,000-$50,099, midpoint 50,049.50: + # 5,700 x 2% + 5,000 x 3% + 10,700 x 3.4% + (50,049.50 - 27,200) x 3.7% + # = 627.80 + 845.43 = 1,473.23 -> 1,473 + ar_income_tax_before_non_refundable_credits_joint: 1_473 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/integration.yaml index f12cef5c9b3..eb4df3bacd3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ar/tax/income/integration.yaml @@ -91,8 +91,16 @@ household: members: [person1, person2] state_fips: 5 # AR - output: # expected results from patched TAXSIM35 2024-02-09 version - ar_income_tax: 4427.88 + output: + # Patched TAXSIM35 (2024-02-09) gives 4,427.88 from the bracket formula at + # the exact incomes. Filing separately, each spouse looks up the 2021 + # Regular Income Tax Table instead: + # person1: 77,010 + 5,505 - 2,200 = 80,315, row $80,301-$80,401: $3,943 (p.32) + # (midpoint 80,351 x 5.9% - 797.25 = 3,943.46 -> 3,943) + # person2: 20,010 + 5,505 - 2,200 = 23,315, row $23,300-$23,400: $546 (p.29) + # (midpoint 23,350 x 3.4% - 248.17 = 545.73 -> 546) + # 3,943 + 546 = 4,489, less 2 x 29 personal credits = 4,431 + ar_income_tax: 4_431 - name: Tax unit with taxsimid 77552 in e21.its.csv and e21.ots.csv # High error margin because of rounding in the tax tables @@ -196,7 +204,14 @@ members: [person1, person2, person3] state_fips: 5 output: - ar_income_tax: 1_085 + # Each spouse: 10,068.22 + 28,857.35 x 50% + 4,218.73 = 28,715.63 of + # gross income, less the 2,410 standard deduction = 26,305.63. Filing + # separately, each looks up the 2024 Regular Income Tax Table row + # $26,300-$26,400: $618 (p.31; midpoint 26,349.50 x 3.9% - 409.86 = 617.77). + # 2 x 618 = 1,236, less 3 x 29 personal credits and 2 x 30 additional + # credits (60 - ceil(505.63 / 100) x 5) = 1,236 - 147 = 1,089. The bracket + # formula at the exact incomes gave 2 x 616.06 - 147 = 1,085.12. + ar_income_tax: 1_089 - name: 3864-AR.yaml absolute_error_margin: 2 @@ -423,19 +438,19 @@ # 40,000 is above the low income table, so the regular table applies. ar_uses_low_income_tax_tables: false ar_net_taxable_income_joint: [35_180, 0] - # 35,180 x 3.9% - 409.86 = 1,372.02 - 409.86 = 962.16 - # The model uses the bracket formula. The 2024 tax table band - # 35,100-35,200 gives 961 (p.31), or 903 after credits; the gap between - # the formula and the table is at most about 2.45 below 100,000. - ar_income_tax_before_non_refundable_credits_joint: [962.16, 0] - # Separately, person1 alone: (60,000 - 2,410) x 3.9% - 409.86 = 1,836.15. + # 2024 Regular Income Tax Table row $35,100-$35,200: $961 (p.31). + # Midpoint 35,149.50 x 3.9% - 409.86 = 1,370.83 - 409.86 = 960.97 -> 961 + # (the bracket formula at the exact income gave 962.16) + ar_income_tax_before_non_refundable_credits_joint: [961, 0] + # Person2's total income is negative, so the couple must file jointly + # (2024 AR1000F instructions, p.12). ar_files_separately: false - ar_income_tax_before_non_refundable_credits_unit: 962.16 + ar_income_tax_before_non_refundable_credits_unit: 961 # Personal credits 2 x 29 = 58; additional credit # 120 - ceil((35,180 - 25,800) / 100) x 5 x 2 = 120 - 940 -> 0. ar_non_refundable_credits: 58 - # 962.16 - 58 = 904.16 - ar_income_tax: 904.16 + # 961 - 58 = 903 + ar_income_tax: 903 - name: Married joint couple, spouse loss offsets wages, positive combined income on the low income table absolute_error_margin: 0.01 @@ -505,7 +520,8 @@ ar_uses_low_income_tax_tables: false ar_net_taxable_income_joint: [0, 0] ar_income_tax_before_non_refundable_credits_joint: [0, 0] - # Separately, person1 alone: (20,000 - 2,410) x 3.4% - 281.37 = 316.69. + # Person2's total income is negative, so the couple must file jointly + # (2024 AR1000F instructions, p.12). ar_files_separately: false ar_income_tax_before_non_refundable_credits_unit: 0 ar_income_tax: 0 @@ -538,7 +554,8 @@ ar_uses_low_income_tax_tables: false ar_net_taxable_income_joint: [0, 0] ar_income_tax_before_non_refundable_credits_joint: [0, 0] - # Separately, person1 alone: (10,000 - 2,410) x 2% - 109.98 = 41.82. + # Person2's total income is negative, so the couple must file jointly + # (2024 AR1000F instructions, p.12). ar_files_separately: false ar_income_tax_before_non_refundable_credits_unit: 0 ar_income_tax: 0 @@ -569,7 +586,8 @@ ar_low_income_tax_joint: [0, 0] ar_net_taxable_income_joint: [0, 0] ar_income_tax_before_non_refundable_credits_joint: [0, 0] - # Separately, person1 alone: (30,000 - 2,410) x 3.9% - 409.86 = 666.15. + # Person2's total income is negative, so the couple must file jointly + # (2024 AR1000F instructions, p.12). ar_files_separately: false ar_income_tax_before_non_refundable_credits_unit: 0 # Flooring each spouse at zero would tax the 30,000 on its own: @@ -603,15 +621,16 @@ ar_taxable_income_joint: [35_180, 0] ar_uses_low_income_tax_tables: false ar_net_taxable_income_joint: [35_180, 0] - # 35,180 x 3.9% - 409.86 = 962.16 - ar_income_tax_before_non_refundable_credits_joint: [962.16, 0] - # Separately, person2 alone: (60,000 - 2,410) x 3.9% - 409.86 = 1,836.15. + # 2024 Regular Income Tax Table row $35,100-$35,200: $961 (p.31). + ar_income_tax_before_non_refundable_credits_joint: [961, 0] + # Person1 (the head) has a negative total income, so the couple must + # file jointly (2024 AR1000F instructions, p.12). ar_files_separately: false - ar_income_tax_before_non_refundable_credits_unit: 962.16 + ar_income_tax_before_non_refundable_credits_unit: 961 # Personal credits 2 x 29 = 58; the additional credit phases out to 0. ar_non_refundable_credits: 58 - # 962.16 - 58 = 904.16 - ar_income_tax: 904.16 + # 961 - 58 = 903 + ar_income_tax: 903 - name: Married joint couple in 2021, partnership loss is in the earlier gross income source list absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/contrib/states/ar/dependent_credit_reform_test.yaml b/policyengine_us/tests/policy/contrib/states/ar/dependent_credit_reform_test.yaml index 5f2473f69f6..f9800b94721 100644 --- a/policyengine_us/tests/policy/contrib/states/ar/dependent_credit_reform_test.yaml +++ b/policyengine_us/tests/policy/contrib/states/ar/dependent_credit_reform_test.yaml @@ -116,8 +116,10 @@ # End-to-end: with the default (no-op) amount, the personal credit (head 29 + # 2 deps * 29 = 87) is consumed via `adds` into ar_personal_credits and reduces # AR income tax. This proves the shared-base-redirect dependent credit is wired - # through to the actual tax, not just the credit aggregate. - output: {ar_income_tax: 1346.67} + # through to the actual tax, not just the credit aggregate. Net taxable + # income 50,000 - 2,470 = 47,530 falls in the 2025 Regular Income Tax Table + # row $47,500-$47,600: $1,434 (AR1000F instructions p.32); 1,434 - 87 = 1,347. + output: {ar_income_tax: 1347} - name: AR dependent credit reform - amount zero raises income tax (downstream end-to-end) period: 2025 @@ -133,9 +135,9 @@ tax_units: {tax_unit: {members: [parent, child1, child2], filing_status: SINGLE}} households: {household: {members: [parent, child1, child2], state_name: AR}} # End-to-end: removing the dependent portion (amount 0) drops the personal - # credit from 87 to 29, raising AR income tax by exactly 58 (1346.67 -> 1404.67). + # credit from 87 to 29, raising AR income tax by exactly 58 (1347 -> 1405). # Confirms the dependent-credit -> tax wiring under the shared-base-redirect pattern. - output: {ar_income_tax: 1404.67} + output: {ar_income_tax: 1405} - name: AR dependent credit reform - age limit excludes dependents at or over the age period: 2025 diff --git a/policyengine_us/tests/test_ar_income_tax_schedule.py b/policyengine_us/tests/test_ar_income_tax_schedule.py index 7fab5095a74..3e3bd69e314 100644 --- a/policyengine_us/tests/test_ar_income_tax_schedule.py +++ b/policyengine_us/tests/test_ar_income_tax_schedule.py @@ -1,7 +1,8 @@ """Arkansas regular income tax schedule: differential and invariant tests. -`ar_main_income_tax` (shared by the indiv and joint tax variables and -`ar_uses_low_income_tax_tables`) has two paths: +`ar_main_income_tax` (the bracket formula that `ar_regular_income_tax` applies +at Regular Income Tax Table row midpoints for the indiv and joint tax variables +and `ar_uses_low_income_tax_tables`) has two paths: - Years with a published DFA Indexed Tax Brackets card (2021-2025) compute `rate.calc(ti) * ti - reduction.calc(ti)` from two single_amount scales @@ -46,6 +47,10 @@ - A reform to the rate table in a projected year keeps tax from falling anywhere except where (A) meets (B)-(C), because the reduction is derived rather than stored. + +`ar_regular_income_tax` reproduces published Regular Income Tax Table rows for +2014-2025, switches to the formula at each year's table threshold, and below +it gives whole dollars that change only at a row start. """ from types import SimpleNamespace @@ -57,6 +62,7 @@ from policyengine_us.system import system from policyengine_us.variables.gov.states.ar.tax.income.ar_income_tax_helpers import ( ar_main_income_tax, + ar_regular_income_tax, ) P = system.parameters.gov.states.ar.tax.income.rates.main @@ -246,7 +252,9 @@ def test_ar_published_reduction_applies_through_2025_only(): @pytest.mark.parametrize("year", [2000, 2013]) def test_ar_schedule_before_modeled_years_still_computes(year): # rate.yaml starts in 2014; earlier years must not hit the statutory path, - # whose parameters start in 2026. + # whose parameters start in 2026. This covers ar_main_income_tax only: + # ar_regular_income_tax reads the tax_table parameters, which start with + # the 2014 table, so it raises for earlier years (see its docstring). assert bool(P.use_published_reduction(f"{year}-01-01")) assert (model_tax(year, TAXABLE_INCOME) == 0).all() @@ -394,3 +402,144 @@ def test_ar_rate_table_reform_keeps_projected_tax_from_falling(): falls = ti[:-1][change < -1e-9] seam = reformed.high_income.threshold("2027-01-01") assert set(falls) <= {seam}, falls + + +# Regular Income Tax Table: below the table threshold, filers look up their net +# taxable income in $100 rows ("As Much As" / "But Less Than"), each computed +# at the row midpoint and rounded to whole dollars. Rows start $1 above the +# hundred from $75,000. Transcribed from each year's DFA table; the last +# element of each row is the file page. +TAX_TABLE_SOURCES = { + 2014: "https://www.dfa.arkansas.gov/wp-content/uploads/LongBookwTaxTables_2014.pdf", + 2015: "https://www.dfa.arkansas.gov/wp-content/uploads/TaxTables_2015.pdf", + 2016: "https://www.dfa.arkansas.gov/wp-content/uploads/LongBookwTaxTables_2016.pdf", + 2017: "https://www.dfa.arkansas.gov/wp-content/uploads/AR1000FandAR1000NRInstructions.pdf", + 2018: "https://www.dfa.arkansas.gov/wp-content/uploads/2018_Final_TaxTable_with_Cover_Sheet.pdf", + **{ + year: f"https://www.dfa.arkansas.gov/wp-content/uploads/{year}_AR1000F_and_AR1000NR_Instructions.pdf" + for year in range(2019, 2026) + }, +} +# (As Much As, But Less Than, tax, file page). 2021 rounds its 50-cent +# midpoints up ($40,250: $1,577.50 -> $1,578); 2022 and 2023 round them down +# ($24,750: $584.50 -> $584; $34,550: $1,026.50 -> $1,026). +TAX_TABLE_ROWS = { + 2014: [(100, 200, 1, 28), (35_000, 35_100, 1_509, 30), (49_900, 50_000, 2_552, 30)], + 2015: [(100, 200, 1, 4), (35_000, 35_100, 1_507, 6), (49_900, 50_000, 2_547, 6)], + 2016: [ + (100, 200, 1, 46), + (35_000, 35_100, 1_369, 48), + (74_900, 75_001, 3_762, 50), + (75_001, 75_101, 3_829, 50), + (85_901, 86_001, 5_021, 50), + ], + 2017: [ + (100, 200, 1, 46), + (35_000, 35_100, 1_356, 48), + (74_900, 75_001, 3_737, 50), + (75_001, 75_101, 3_743, 50), + (85_901, 86_001, 4_990, 50), + ], + 2018: [ + (100, 200, 1, 4), + (35_000, 35_100, 1_347, 6), + (74_900, 75_001, 3_719, 8), + (75_001, 75_101, 3_725, 8), + (85_901, 86_001, 4_968, 8), + ], + 2019: [ + (4_600, 4_700, 1, 28), + (35_000, 35_100, 1_331, 29), + (74_900, 75_001, 3_697, 31), + (75_001, 75_101, 3_703, 31), + (86_901, 87_001, 5_018, 32), + ], + 2020: [ + (4_700, 4_800, 1, 28), + (35_000, 35_100, 1_325, 29), + (74_900, 75_001, 3_648, 31), + (75_001, 75_101, 3_654, 31), + (87_901, 88_001, 4_989, 32), + ], + 2021: [ + (4_800, 4_900, 1, 28), + (35_000, 35_100, 1_313, 29), + (40_200, 40_300, 1_578, 30), + (74_900, 75_001, 3_625, 31), + (75_001, 75_101, 3_631, 31), + (90_901, 91_001, 5_119, 32), + ], + 2022: [ + (5_100, 5_200, 1, 31), + (24_700, 24_800, 584, 32), + (35_000, 35_100, 1_089, 32), + (74_900, 75_001, 3_044, 34), + (75_001, 75_101, 3_049, 34), + (93_901, 94_001, 4_436, 35), + ], + 2023: [ + (5_300, 5_400, 1, 31), + (34_500, 34_600, 1_026, 32), + (35_000, 35_100, 1_050, 32), + (74_900, 75_001, 2_925, 34), + (75_001, 75_101, 2_930, 34), + (99_901, 100_001, 4_544, 35), + ], + 2024: [ + (5_500, 5_600, 1, 30), + (35_000, 35_100, 957, 31), + (35_100, 35_200, 961, 31), + (74_900, 75_001, 2_513, 33), + (75_001, 75_101, 2_517, 33), + (99_901, 100_001, 3_811, 34), + ], + 2025: [ + (5_600, 5_700, 1, 30), + (35_000, 35_100, 947, 31), + (74_900, 75_001, 2_503, 33), + (75_001, 75_101, 2_507, 33), + (99_901, 100_001, 3_809, 34), + ], +} + + +@pytest.mark.parametrize( + "year, low, high, tax, page", + [(year, *row) for year, rows in TAX_TABLE_ROWS.items() for row in rows], +) +def test_ar_regular_income_tax_matches_published_table_rows(year, low, high, tax, page): + incomes = np.array([low, (low + high) / 2, high - 1, high - 0.01], dtype=float) + model = ar_regular_income_tax(incomes, P(f"{year}-01-01")) + assert (model == tax).all(), ( + f"{TAX_TABLE_SOURCES[year]}#page={page}: row {low:,}-{high:,} is {tax:,}, " + f"model gives {model}" + ) + + +@pytest.mark.parametrize("year", range(2014, 2026)) +def test_ar_regular_income_tax_table_threshold_is_the_last_published_row(year): + # The last row's "But Less Than" bound is where the formula takes over. + p = P(f"{year}-01-01") + assert p.tax_table.threshold == TAX_TABLE_ROWS[year][-1][1] + at_and_above = np.array([0, 0.5, 10_000]) + p.tax_table.threshold + assert np.allclose( + ar_regular_income_tax(at_and_above, p), ar_main_income_tax(at_and_above, p) + ) + + +@pytest.mark.parametrize("year", list(range(2014, 2036))) +def test_ar_regular_income_tax_is_whole_dollars_constant_within_each_table_row(year): + p = P(f"{year}-01-01") + threshold = p.tax_table.threshold + ti = np.arange(0, threshold, 0.25) + tax = ar_regular_income_tax(ti, p) + assert (tax == np.round(tax)).all() + # Tax changes only at a row start: a multiple of $100, plus $1 from $75,000. + changes = ti[1:][np.diff(tax) != 0] + offset = p.tax_table.band_offset.calc(changes) + row_start = (changes - offset) % p.tax_table.band_width == 0 + # The one row wider than $100 ($74,900 to $75,001) is evaluated at + # midpoints a dollar apart on either side of $75,000, which can round + # differently. + offset_start = np.isin(changes, p.tax_table.band_offset.thresholds[1:]) + assert (row_start | offset_start).all(), changes diff --git a/policyengine_us/variables/gov/states/ar/tax/income/ar_files_separately.py b/policyengine_us/variables/gov/states/ar/tax/income/ar_files_separately.py index 239ac6ea49f..16187a590be 100644 --- a/policyengine_us/variables/gov/states/ar/tax/income/ar_files_separately.py +++ b/policyengine_us/variables/gov/states/ar/tax/income/ar_files_separately.py @@ -9,6 +9,10 @@ class ar_files_separately(Variable): reference = ( "https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_and_AR1000NR_Instructions.pdf", "https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_FullYearResidentIndividualIncomeTaxReturn.pdf", + # Filing Status 4: "IF ONE SPOUSE HAD A TOTAL NEGATIVE INCOME, YOU + # MUST FILE MARRIED FILING JOINTLY." + "https://www.dfa.arkansas.gov/wp-content/uploads/2024_AR1000F_and_AR1000NR_Instructions.pdf#page=12", + "https://www.dfa.arkansas.gov/wp-content/uploads/2025_AR1000F_and_AR1000NR_Instructions.pdf#page=12", ) defined_for = StateCode.AR @@ -67,6 +71,13 @@ def formula(tax_unit, period, parameters): married = (filing_status == statuses.JOINT) | ( filing_status == statuses.SEPARATE ) + # A couple must file jointly if either spouse had a total negative + # income. Total income (line 23) is each spouse's column of gross + # income less the exemptions subtracted within it, before any floor. + total_income = person("ar_gross_income_indiv", period) - person( + "ar_exemptions", period + ) + negative_total_income = tax_unit.any(head_or_spouse & (total_income < 0)) # Compare after filing-method-sensitive credits. separate_cheaper = (itax_indiv - credit_sep) < (itax_joint - credit_joint) - return married & separate_cheaper + return married & ~negative_total_income & separate_cheaper diff --git a/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_indiv.py b/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_indiv.py index 6a8bd7885e0..16718c93f7b 100644 --- a/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_indiv.py +++ b/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_indiv.py @@ -1,6 +1,6 @@ from policyengine_us.model_api import * from policyengine_us.variables.gov.states.ar.tax.income.ar_income_tax_helpers import ( - ar_main_income_tax, + ar_regular_income_tax, ) @@ -13,10 +13,14 @@ class ar_income_tax_before_non_refundable_credits_indiv(Variable): reference = ( "https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_and_AR1000NR_Instructions.pdf", "https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_FullYearResidentIndividualIncomeTaxReturn.pdf", + # Regular Income Tax Table. + "https://www.dfa.arkansas.gov/wp-content/uploads/2024_AR1000F_and_AR1000NR_Instructions.pdf#page=30", + # Indexed Tax Brackets: the table is computed at each row's midpoint. + "https://www.dfa.arkansas.gov/wp-content/uploads/2024_TaxBrackets.pdf#page=2", ) defined_for = StateCode.AR def formula(person, period, parameters): p = parameters(period).gov.states.ar.tax.income.rates.main taxable_income = person("ar_taxable_income_indiv", period) - return max_(ar_main_income_tax(taxable_income, p), 0) + return max_(ar_regular_income_tax(taxable_income, p), 0) diff --git a/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_joint.py b/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_joint.py index 708fb778983..7241c6983a6 100644 --- a/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_joint.py +++ b/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_before_non_refundable_credits_joint.py @@ -1,6 +1,6 @@ from policyengine_us.model_api import * from policyengine_us.variables.gov.states.ar.tax.income.ar_income_tax_helpers import ( - ar_main_income_tax, + ar_regular_income_tax, ) @@ -15,12 +15,16 @@ class ar_income_tax_before_non_refundable_credits_joint(Variable): reference = ( "https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_and_AR1000NR_Instructions.pdf", "https://www.dfa.arkansas.gov/wp-content/uploads/2023_AR1000F_FullYearResidentIndividualIncomeTaxReturn.pdf", + # Regular Income Tax Table. + "https://www.dfa.arkansas.gov/wp-content/uploads/2024_AR1000F_and_AR1000NR_Instructions.pdf#page=30", + # Indexed Tax Brackets: the table is computed at each row's midpoint. + "https://www.dfa.arkansas.gov/wp-content/uploads/2024_TaxBrackets.pdf#page=2", ) defined_for = StateCode.AR def formula(person, period, parameters): p = parameters(period).gov.states.ar.tax.income.rates.main taxable_income = person("ar_taxable_income_joint", period) - total_main_rate = max_(ar_main_income_tax(taxable_income, p), 0) + total_main_rate = max_(ar_regular_income_tax(taxable_income, p), 0) low_income_tax = person("ar_low_income_tax_joint", period) return min_(total_main_rate, low_income_tax) diff --git a/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_helpers.py b/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_helpers.py index d03274be3c8..d028bdaf3fb 100644 --- a/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_helpers.py +++ b/policyengine_us/variables/gov/states/ar/tax/income/ar_income_tax_helpers.py @@ -37,6 +37,43 @@ def ar_main_income_tax(taxable_income, p): ) +def ar_regular_income_tax(taxable_income, p): + """Arkansas regular income tax as the Regular Income Tax Table gives it. + + `p` is `parameters(period).gov.states.ar.tax.income.rates.main`. + + Below the table threshold, filers look up their net taxable income in the + Regular Income Tax Table. DFA computes each row by applying the bracket + formula at the midpoint of the row's income band and rounding to whole + dollars. At and above the threshold, the formula applies to the exact + income. The result is not floored at zero. + + The `tax_table` parameters start with the 2014 table, so unlike + `ar_main_income_tax` (which returns 0 there), this helper has no parameter + values before 2014 and raises `ParameterNotFoundError`. Computed from + household inputs, the Arkansas income tax never reaches those years: the + standard deduction has no value before 2015, the first year the model + computes. + """ + table = p.tax_table + width = table.band_width + offset = table.band_offset.calc(taxable_income) + band_start = np.floor((taxable_income - offset) / width) * width + offset + band_end = band_start + width + if table.whole_dollar_midpoint: + # Rows cover whole-dollar incomes from band_start up to one dollar + # below the next row. + band_end = band_end - 1 + midpoint = (band_start + band_end) / 2 + # Round half up to whole dollars. + table_tax = np.floor(ar_main_income_tax(midpoint, p) + 0.5) + return where( + taxable_income < table.threshold, + table_tax, + ar_main_income_tax(taxable_income, p), + ) + + def marginal_tax(rate_scale, income): """Tax at the rates of a single_amount scale keyed by each row's lower bound, applied to the income within each row.""" diff --git a/policyengine_us/variables/gov/states/ar/tax/income/ar_uses_low_income_tax_tables.py b/policyengine_us/variables/gov/states/ar/tax/income/ar_uses_low_income_tax_tables.py index 8e7563c3cb2..aa07cbb681b 100644 --- a/policyengine_us/variables/gov/states/ar/tax/income/ar_uses_low_income_tax_tables.py +++ b/policyengine_us/variables/gov/states/ar/tax/income/ar_uses_low_income_tax_tables.py @@ -1,6 +1,6 @@ from policyengine_us.model_api import * from policyengine_us.variables.gov.states.ar.tax.income.ar_income_tax_helpers import ( - ar_main_income_tax, + ar_regular_income_tax, ) @@ -20,7 +20,7 @@ def formula(tax_unit, period, parameters): p = parameters(period).gov.states.ar.tax.income.rates.main person = tax_unit.members taxable_income = person("ar_taxable_income_joint", period) - total_main_rate_person = max_(ar_main_income_tax(taxable_income, p), 0) + total_main_rate_person = max_(ar_regular_income_tax(taxable_income, p), 0) total_main_rate = tax_unit.sum(total_main_rate_person) low_income_tax = add(tax_unit, period, ["ar_low_income_tax_joint"]) return total_main_rate > low_income_tax