4. Application Exercise – Tax Calculation
4.1. The Problem
We aim to write a program to calculate a taxpayer’s tax. We’ll consider the simplified case of a taxpayer who has only their salary to report:
- The number of shares for the employee is calculated as nbParts = nbEnfants / 2 + 1 if they are unmarried, or nbEnfants / 2 + 2 if they are married, where nbEnfants is the number of children they have.
- We calculate their taxable income R = 0.72 * S, where S is their annual salary
- We calculate their family coefficient Q = R / N
We calculate their tax I based on the following data
12620.0 | 0 | 0 |
13,190 | 0.05 | 631 |
15,640 | 0.1 | 1,290.5 |
24,740 | 0.15 | 2,072.5 |
31,810 | 0.2 | 3,309.5 |
39,970 | 0.25 | 4900 |
48,360 | 0.3 | 6,898.5 |
55,790 | 0.35 | 9,316.5 |
92,970 | 0.4 | 12,106 |
127,860 | 0.45 | 16,754.5 |
151,250 | 0.50 | 23,147.5 |
172,040 | 0.55 | 30,710 |
195,000 | 0.60 | 39,312 |
0 | 0.65 | 49,062 |
Each row has 3 fields. To calculate tax I, we look for the first row where QF <= field1. For example, if QF = 30000, we will find the row
Tax I is then equal to 0.15*R - 2072.5*nbParts. If QF is such that the condition QF<=field1 is never met, then the coefficients from the last row are used. Here:
which gives tax I = 0.65*R – 49062*nbParts.
4.2. Version with arrays (impots_01)
We present an initial program where:
- the data needed to calculate the tax are in three arrays (limits, coeffR, coeffN)
- the taxpayer data (married, children, salary) are in a first text file
- the results of the tax calculation (married, children, salary, tax) are stored in a second text file
<?php
// definition of constants
$DATA = "data.txt";
$RESULTS = "results.txt";
$limits = array(12620, 13190, 15640, 24740, 31810, 39970, 48360, 55790, 92970, 127860, 151250, 172040, 195000);
$coeffR = array(0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.55, 0.6, 0.65);
$coeffN = array(0, 631, 1290.5, 2072.5, 3309.5, 4900, 6898.5, 9316.5, 12106, 16754.5, 23147.5, 30710, 39312, 49062);
// Read data
$data = fopen($DATA, "r");
if (!$data) {
print "Unable to open the data file [$DATA] for reading\n";
exit;
}
// Open the results file
$results = fopen($RESULTS, "w");
if (!$results) {
print "Unable to create the results file [$RESULTS]\n";
exit;
}
// process the current line of the data file
while ($line = fgets($data, 100)) {
// remove any end-of-line characters
$line = cutNewLineChar($line);
// retrieve the 3 fields married, children, salary that make up $line
list($married, $children, $salary) = explode(",", $line);
// calculate the tax
$tax = calculateTaxes($married, $children, $salary, $limits, $coeffR, $coeffN);
// write the result to the results file
fputs($results, "$married:$children:$salary:$tax\n");
// next taxpayer
}
// close the files
fclose($data);
fclose($results);
// end
exit;
// --------------------------------------------------------------------------
function cutNewLinechar($line) {
// remove the end-of-line character from $line if it exists
$L = strlen($line); // line length
while (substr($line, $L - 1, 1) == "\n" or substr($line, $L - 1, 1) == "\r") {
$line = substr($line, 0, $L - 1);
$L--;
}
// end
return($line);
}
// --------------------------------------------------------------------------
function calculateTaxes($married, $children, $salary, $thresholds, $coeffR, $coeffN) {
// $married: yes, no
// $children: number of children
// $salary: annual salary
// $limits, $coeffR, $coeffN: data arrays used to calculate taxes
// number of shares
$married = strtolower($married);
if ($married == "yes")
$nbParts = $children / 2 + 2;
else
$nbParts = $children / 2 + 1;
// add 1/2 share if there are at least 3 children
if ($children >= 3)
$nbParts+=0.5;
// taxable income
$taxableIncome = 0.72 * $salary;
// family quotient
$quotient = $taxableIncome / $numberOfChildren;
// is added to the end of the limits array to stop the following loop
array_push($limits, $quotient);
// tax calculation
$i = 0;
while ($quotient > $limits[$i])
$i++;
// Since we placed $quotient at the end of the $limites array, the previous loop
// cannot go beyond the bounds of the $limits array
// now we can calculate the tax
return floor($taxableIncome * $rate[$i] - $numberOfShares * $taxRate[$i]);
}
?>
The data file data.txt (married, children, salary):
The results.txt files (married, children, salary, tax) for the results obtained:
yes:2:200000:22504
no:2:200000:33388
yes:3:200000:16400
no:3:200000:22504
yes:5:50000:0
no:0:3000000:1354938
Comments
- line 4: the name of the text file containing taxpayer data (married, children, salary)
- line 5: the name of the text file containing the results (married, children, salary, tax) of the tax calculation
- Lines 6–8: the three data tables used to calculate the tax
- lines 11-15: open the taxpayer data file for reading
- lines 18–22: open the results file for writing
- line 25: loop to read the lines (married, children, salary) from the taxpayer file
- line 27: the end-of-line character is removed
- line 29: the components (married, children, salary) of the line are retrieved
- line 31: tax is calculated
- line 33: the tax is stored in the results file
- lines 37-38: once the taxpayer file has been fully processed, the files are closed
- line 44: the function that removes the end-of-line character from a line $line. The end-of-line character is the string "\r\n" on Windows systems, "\n" on Unix systems. The result is the input string without its end-of-line character.
- lines 47-48: substr($string, $start, $size) returns the substring of $string starting at character $start and containing at most $size characters.
- line 63: strtolower($string) converts $string to lowercase
- line 76: array_push($array, $element) adds $element to the end of the $array. Note that the $limits array is passed by value to the calculateTax function (line 56). This addition of an element is therefore performed on a copy of the actual parameter, which remains unchanged.
- line 84: the function floor($x) returns the integer value immediately less than $x.
4.3. Version with text files (impots_02)
This new version differs from the previous one only in that the data ($limites, $coeffR, $coeffN) needed to calculate the tax are now in a text file in the following format:
File impots.txt:
12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:2072.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062
The new version simply reads the data from the [impots.txt] file and places it into three arrays ($limites, $coeffR, $coeffN). This brings us back to the previous case. We present only the changes:
<?php
// definition of constants
$DATA = "data.txt";
$RESULTS = "results.txt";
$TAXES = "taxes.txt";
// The data needed to calculate the tax has been placed in the $IMPOTS file
// with one line per table in the form
// val1:val2:val3,...
list($error, $limits, $coeffR, $coeffN) = getTables($TAXES);
// Was there an error?
if ($error) {
print "$error\n";
exit;
}//if
// reading user data
...
// end
exit;
// --------------------------------------------------------------------------
function cutNewLinechar($line) {
// remove the end-of-line character from $line if it exists
...
}
// --------------------------------------------------------------------------
function calculateTaxes($married, $children, $salary, $thresholds, $coeffR, $coeffN) {
// $married: yes, no
// $children: number of children
// $salary: annual salary
// $limits, $coeffR, $coeffN: data arrays used to calculate taxes
...
}
// --------------------------------------------------------------------------
function getTables($IMPOTS) {
// $IMPOTS: the name of the file containing the data for the $limites, $coeffR, and $coeffN tables
// Does the $IMPOTS file exist?
if (!file_exists($IMPOTS))
return array("The $IMPOTS file does not exist","","","");
// read the file in blocks
$tables = file($IMPOTS);
if (!$tables)
return array("Error while processing the $IMPOTS file","","","");
// Create the 3 tables - assuming the lines are syntactically correct
$limits = explode(":", cutNewLineChar($tables[0]));
$coeffR = explode(":", cutNewLineChar($tables[1]));
$coeffN = explode(":", cutNewLineChar($tables[2]));
// end
return array("", $limits, $coeffR, $coeffN);
}
?>
Comments
- Line 11: The function getTables($IMPOTS) processes the text file $IMPOTS to extract the three tables ($limites, $coeffR, $coeffN). The result returned is ($error, $limites, $coeffR, $coeffN), where $error is an error message; it is empty if no error occurred.
- Line 40: The getTables($IMPOTS) function.
- line 43: the function files_exists(filename) returns the boolean true if the file filename exists, and false otherwise.
- line 46: the file(filename) function reads the entire text file filename. It returns an array where each element is a line from the text file. Since the impots.txt file has three lines of text, the function will return an array with three elements.
- Lines 50–52: Use the three retrieved lines to create the three arrays ($limits, $coeffR, $coeffN)
The results obtained are the same as those obtained in the previous version.