Case Study 1: Building a Tax Calculator
Progressive tax bracket calculation using nested IF and CASE — real-world decision logic with monetary calculations.
The Scenario
Rosa Martinelli is preparing her freelance income taxes. As a graphic designer, she earns variable income each month, and she needs to understand how progressive taxation actually works. She has heard people say "I don't want a raise because it will put me in a higher tax bracket" — but she suspects this reflects a misunderstanding of how tax brackets function.
Rosa decides to build a tax calculator in Pascal. Not only will it compute her taxes, but it will also show her exactly how much tax applies to each bracket, proving once and for all that a raise never makes you worse off.
The Problem
The United States federal income tax uses a progressive bracket system. For a single filer in 2024, the brackets are:
| Bracket | Taxable Income Range | Marginal Rate |
|---|---|---|
| 1 | $0 – $11,000 | 10% |
| 2 | $11,001 – $44,725 | 12% |
| 3 | $44,726 – $95,375 | 22% |
| 4 | $95,376 – $182,100 | 24% |
| 5 | $182,101 – $231,250 | 32% |
| 6 | $231,251 – $578,125 | 35% |
| 7 | Over $578,125 | 37% |
Key insight: "Progressive" means each rate applies only to the income within that bracket. Someone earning $50,000 does not pay 22% on the entire $50,000. They pay 10% on the first $11,000, 12% on the next $33,725 (from $11,001 to $44,725), and 22% only on the remaining $5,275.
The Design
Rosa sketches out her approach:
- Validate input — income must be non-negative.
- Calculate tax per bracket — for each bracket, determine how much income falls within it and apply the rate.
- Accumulate total tax — sum the tax from all applicable brackets.
- Display results — show the breakdown per bracket and the effective rate.
The core logic uses an if..else if chain to determine which brackets apply, but within each bracket, the calculation follows the same pattern:
taxable_in_bracket = min(income, bracket_ceiling) - bracket_floor
tax_in_bracket = taxable_in_bracket * rate
The Implementation
program TaxCalculator;
{ Progressive Income Tax Calculator }
{ Demonstrates: IF..ELSE IF chains, nested IF, compound Boolean, }
{ named constants, formatted output, guard clauses }
const
{ Bracket ceilings }
BRACKET_1_LIMIT = 11000.00;
BRACKET_2_LIMIT = 44725.00;
BRACKET_3_LIMIT = 95375.00;
BRACKET_4_LIMIT = 182100.00;
BRACKET_5_LIMIT = 231250.00;
BRACKET_6_LIMIT = 578125.00;
{ Tax rates }
RATE_1 = 0.10;
RATE_2 = 0.12;
RATE_3 = 0.22;
RATE_4 = 0.24;
RATE_5 = 0.32;
RATE_6 = 0.35;
RATE_7 = 0.37;
var
income: Real;
totalTax: Real;
taxInBracket: Real;
effectiveRate: Real;
taxableInBracket: Real;
begin
WriteLn('==========================================');
WriteLn(' Progressive Income Tax Calculator');
WriteLn(' (2024 Single Filer Brackets)');
WriteLn('==========================================');
WriteLn;
Write('Enter your taxable income: $');
ReadLn(income);
WriteLn;
{ Guard clause: validate input }
if income < 0 then
begin
WriteLn('Error: Income cannot be negative.');
Halt(1);
end;
if income = 0 then
begin
WriteLn('No income, no tax. Total tax: $0.00');
Halt(0);
end;
totalTax := 0.0;
WriteLn('--- Tax Breakdown by Bracket ---');
WriteLn;
{ Bracket 1: 10% on first $11,000 }
if income > 0 then
begin
if income <= BRACKET_1_LIMIT then
taxableInBracket := income
else
taxableInBracket := BRACKET_1_LIMIT;
taxInBracket := taxableInBracket * RATE_1;
totalTax := totalTax + taxInBracket;
WriteLn('Bracket 1 (10%): $', taxableInBracket:10:2,
' -> Tax: $', taxInBracket:10:2);
end;
{ Bracket 2: 12% on $11,001 – $44,725 }
if income > BRACKET_1_LIMIT then
begin
if income <= BRACKET_2_LIMIT then
taxableInBracket := income - BRACKET_1_LIMIT
else
taxableInBracket := BRACKET_2_LIMIT - BRACKET_1_LIMIT;
taxInBracket := taxableInBracket * RATE_2;
totalTax := totalTax + taxInBracket;
WriteLn('Bracket 2 (12%): $', taxableInBracket:10:2,
' -> Tax: $', taxInBracket:10:2);
end;
{ Bracket 3: 22% on $44,726 – $95,375 }
if income > BRACKET_2_LIMIT then
begin
if income <= BRACKET_3_LIMIT then
taxableInBracket := income - BRACKET_2_LIMIT
else
taxableInBracket := BRACKET_3_LIMIT - BRACKET_2_LIMIT;
taxInBracket := taxableInBracket * RATE_3;
totalTax := totalTax + taxInBracket;
WriteLn('Bracket 3 (22%): $', taxableInBracket:10:2,
' -> Tax: $', taxInBracket:10:2);
end;
{ Bracket 4: 24% on $95,376 – $182,100 }
if income > BRACKET_3_LIMIT then
begin
if income <= BRACKET_4_LIMIT then
taxableInBracket := income - BRACKET_3_LIMIT
else
taxableInBracket := BRACKET_4_LIMIT - BRACKET_3_LIMIT;
taxInBracket := taxableInBracket * RATE_4;
totalTax := totalTax + taxInBracket;
WriteLn('Bracket 4 (24%): $', taxableInBracket:10:2,
' -> Tax: $', taxInBracket:10:2);
end;
{ Bracket 5: 32% on $182,101 – $231,250 }
if income > BRACKET_4_LIMIT then
begin
if income <= BRACKET_5_LIMIT then
taxableInBracket := income - BRACKET_4_LIMIT
else
taxableInBracket := BRACKET_5_LIMIT - BRACKET_4_LIMIT;
taxInBracket := taxableInBracket * RATE_5;
totalTax := totalTax + taxInBracket;
WriteLn('Bracket 5 (32%): $', taxableInBracket:10:2,
' -> Tax: $', taxInBracket:10:2);
end;
{ Bracket 6: 35% on $231,251 – $578,125 }
if income > BRACKET_5_LIMIT then
begin
if income <= BRACKET_6_LIMIT then
taxableInBracket := income - BRACKET_5_LIMIT
else
taxableInBracket := BRACKET_6_LIMIT - BRACKET_5_LIMIT;
taxInBracket := taxableInBracket * RATE_6;
totalTax := totalTax + taxInBracket;
WriteLn('Bracket 6 (35%): $', taxableInBracket:10:2,
' -> Tax: $', taxInBracket:10:2);
end;
{ Bracket 7: 37% on everything over $578,125 }
if income > BRACKET_6_LIMIT then
begin
taxableInBracket := income - BRACKET_6_LIMIT;
taxInBracket := taxableInBracket * RATE_7;
totalTax := totalTax + taxInBracket;
WriteLn('Bracket 7 (37%): $', taxableInBracket:10:2,
' -> Tax: $', taxInBracket:10:2);
end;
{ Summary }
effectiveRate := (totalTax / income) * 100.0;
WriteLn;
WriteLn('==========================================');
WriteLn('Taxable Income: $', income:10:2);
WriteLn('Total Tax: $', totalTax:10:2);
WriteLn('After-Tax Income: $', (income - totalTax):10:2);
WriteLn('Effective Rate: ', effectiveRate:5:2, '%');
WriteLn('==========================================');
end.
Sample Output
==========================================
Progressive Income Tax Calculator
(2024 Single Filer Brackets)
==========================================
Enter your taxable income: $75000
--- Tax Breakdown by Bracket ---
Bracket 1 (10%): $ 11000.00 -> Tax: $ 1100.00
Bracket 2 (12%): $ 33725.00 -> Tax: $ 4047.00
Bracket 3 (22%): $ 30275.00 -> Tax: $ 6660.50
==========================================
Taxable Income: $ 75000.00
Total Tax: $ 11807.50
After-Tax Income: $ 63192.50
Effective Rate: 15.74%
==========================================
Analysis
Decision Constructs Used
| Construct | Purpose | Location |
|---|---|---|
Guard clause (if..Halt) |
Rejects negative and zero income early | Lines after input |
Nested if..then..else |
Determines taxable amount within each bracket | Each bracket block |
Sequential if (not else-if) |
Each bracket is evaluated independently | Bracket 1 through 7 |
Why Sequential IF, Not IF..ELSE IF?
Notice that we use sequential if statements, not an if..else if chain. This is deliberate. Each bracket is independent: income of $75,000 falls in brackets 1, 2, and 3. An if..else if chain would stop after the first match, calculating tax for only one bracket. The sequential approach processes all applicable brackets.
This is a critical distinction. Use if..else if when the cases are mutually exclusive (only one can be true). Use sequential if statements when multiple conditions can be true simultaneously.
Why Not CASE?
The tax brackets are defined by Real boundaries, and case requires ordinal types. You could convert income to an integer (cents) and use case with ranges, but the code would be less readable. The if-based approach maps directly to the tax table and is easier to update when brackets change.
The Repetition Problem
Rosa notices that all seven bracket calculations follow the same pattern. The code works, but it is repetitive. In Chapter 7, she will refactor this into a procedure CalculateBracketTax(income, floor, ceiling, rate) that eliminates the duplication. In Chapter 9, she will store the bracket data in arrays and use a loop. For now, the explicit version demonstrates the decision logic clearly.
Discussion Questions
-
Effective Rate vs. Marginal Rate. For $75,000 income, the marginal rate is 22% (the highest bracket reached), but the effective rate is only 15.74%. Why is understanding this difference important? How does the program make this visible?
-
The Myth of "Moving to a Higher Bracket." Someone earning $44,725 pays $5,147 in tax (effective rate: 11.51%). If they get a $1 raise to $44,726, they pay $5,147.22 — only 22 cents more. The raise never costs more than it earns. How does the progressive structure guarantee this?
-
Testing Boundaries. What income values would you use to test this calculator thoroughly? List at least seven specific values and explain why each matters. (Hint: test at each bracket boundary, at zero, at a very large income, and at values just above and below each boundary.)
-
Defensive Programming. The guard clause rejects negative income but does not handle extremely large values. What issues might arise with incomes over $1 trillion? (Hint: consider floating-point precision.)
-
Design Smell. The seven bracket blocks are nearly identical. What principle of software engineering does this repetition violate? How would you fix it using only the tools you have learned so far (Chapters 1-5)?
Extension Challenge
Modify the tax calculator to support two filing statuses (Single and Married Filing Jointly) with different bracket limits. Use a case statement on a character input ('S' or 'M') to select which set of bracket limits to use. Store the limits in variables that are assigned based on the filing status before the tax calculation begins.