#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
/* **************************************************************************
* *
* Helper functions *
* *
* **************************************************************************/
/*
* Get confirmation by y for yes or n for no.
* Prints title and loops until one of the two is given.
* Return 1 for yes, and 0 for no.
* */
static int confirm(char *title)
{
char buf[10];
fprintf(stdout, "%s (y or n) ", title);
while (1) {
fgets(buf, sizeof(buf), stdin);
switch (*buf) {
case 'y': case 'Y':
return 1;
case 'n': case 'N':
return 0;
}
fprintf(stderr, "Please answer with y for Yes or n for No: ");
}
}
/* Crude function for reading float from user input.
* Prints title, can be empty string.
* */
static float get_float(char *title) {
char buf[80];
fputs(title, stdout);
fgets(buf, sizeof(buf), stdin);
return atof(buf);
}
/* Function to read in and summarize floats.
*
* Read and add values until "n" is given as response on request for more.
* We could have used "0" to denote end of input, and that would perhaps
* also be more user friendly.
* */
static float get_info_float(char *type)
{
float amount, total = 0;
fprintf(stdout, "\n -- %s --\n", type);
do {
amount = get_float("\n Enter amount: ");
total += amount;
fprintf(stdout, "\n Do you have any more %s? ", type);
} while(confirm(""));
return total;
}
/* **************************************************************************
* *
* INCOME ADJUSTMENTS *
* *
* **************************************************************************/
/*
* An array for each filing type / status.
* Each index correspond to an income group starting with index 0 for <= 6000
* index 1 for <= 9000 etc.
* */
const float adjust_SH[] = { 0.000, 0.038, 0.074, 0.110, 0.138, 0.154, 0.350 };
const float adjust_S[] = { 0.028, 0.075, 0.096, 0.135, 0.155, 0.174, 0.350 };
const float adjust_MJ[] = { 0.000, 0.052, 0.083, 0.122, 0.146, 0.163, 0.350 };
const float adjust_MS[] = { 0.023, 0.072, 0.089, 0.131, 0.152, 0.172, 0.350 };
/* Income groups
* */
const int income_group[] = { 6000, 9000, 15000, 21000, 25000, 30000 };
/*
* Use defines to make the code both more readable and maintainable.
* */
#define STATUS_S 1
#define STATUS_MJ 2
#define STATUS_MS 3
#define STATUS_SH 4
/* Max and min STATUS_xx values. */
#define STATUS_MIN 1
#define STATUS_MAX 4
/* Length of income group array. */
#define INCOME_GR_LEN 6
/* Get income group based in income.
* Return index which in turn correspond to index of adjust_xx arrays.
* */
static int get_income_group(float income)
{
int i;
/* Here we could have used sizeof(income_group) / INT_SIZE
* instead of having a constant INCOME_GR_LEN
* but not sure if that is stretching the "new information"
* to far.
* */
for (i = 0; i < INCOME_GR_LEN; ++i) {
if (income <= income_group[i])
return i;
}
return i;
}
/*
* Get status / type for account.
* This function could also have been grouped with the other "get_data"
* functions, but found it was better to leave it here.
* */
static int get_status() {
int status = 0;
fputs( "\n"
" Status types:\n"
" 1. Single Filing\n"
" 2. Married Filing Jointly\n"
" 3. Married Filing Separately\n"
" 4. Single Head of Household Filing\n"
"\n"
,
stdout
);
do {
status = (int)get_float("Enter Status type number: ");
} while (status < STATUS_MIN && status > STATUS_MAX);
return status;
}
/* Calculate adjusted income based on income - dep and status.
* */
static float get_income_adjusted(
float inc_dep,
int status
) {
float adjusted_income = 0;
int inc_group;
/* This should likely be fixed. */
assert(inc_dep > 0);
if (inc_dep < 0) {
fprintf(stderr, "FAIL!\n");
return 0.0;
}
inc_group = get_income_group(inc_dep);
switch (status) {
case STATUS_SH:
adjusted_income = inc_dep * adjust_SH[inc_group];
break;
case STATUS_S:
adjusted_income = inc_dep * adjust_S[inc_group];
break;
case STATUS_MJ:
adjusted_income = inc_dep * adjust_MJ[inc_group];
break;
case STATUS_MS:
adjusted_income = inc_dep * adjust_MS[inc_group];
break;
}
return adjusted_income;
}
/* **************************************************************************
* *
* PRINT FUNCTIONS *
* *
* **************************************************************************/
/*
* Print result of one filing.
* */
static void print_result(
float wages,
float income_other,
float interests,
float dividends,
float income_dep,
float income_adj
) {
int hdr_w = 29;
fprintf(stdout,
"\n\n"
"-----------------------------------------------\n"
" %-*s: %.2f\n"
" %-*s: %.2f\n"
" %-*s: %.2f\n"
" %-*s: %.2f\n"
" %-*s: %.2f\n"
"\n"
" %-*s: %.2f\n"
"-----------------------------------------------\n"
"\n"
,
hdr_w, "YOUR WAGES", wages,
hdr_w, "YOUR OTHER INCOME", income_other,
hdr_w, "YOUR INTEREST", interests,
hdr_w, "YOUR DIVIDEND", dividends,
hdr_w, "YOUR INCOME AFTER DEPENDANTS", income_dep,
hdr_w,
(
income_adj < 0 ?
"Your income tax RETURN is" :
"Your income tax OWED is"
),
income_adj
);
}
/* Print final statistics at exit.
* */
static void print_filing_stats(
int files_s,
int files_mj,
int files_ms,
int files_sh
) {
int hdr_w = 44;
fprintf(stdout,
"%-*s: %i\n"
"%-*s: %i\n"
"%-*s: %i\n"
"%-*s: %i\n"
,
hdr_w, "Number of Singles Filing", files_s,
hdr_w, "Number of Married Filing Jointly", files_mj,
hdr_w, "Number of Married Filing Separately",files_ms,
hdr_w, "Number of Single Head of Household Filing", files_sh
);
}
/* **************************************************************************
* *
* DATA PROCESSING *
* *
* **************************************************************************/
#define DEP_FACTOR 2800
int process_filing()
{
float wages,
income_other,
interests,
dividends,
income_dep,
income_adj,
sum;
int dependents,
status;
/* Get the various accounting posts. */
wages = get_info_float("Wages");
income_other = get_info_float("Other income");
interests = get_info_float("Interest");
dividends = get_info_float("Dividends");
dependents = DEP_FACTOR * (int)get_info_float("Dependents");
/* Calculate income minus dependents */
sum = wages + income_other + interests + dividends;
income_dep = sum - dependents;
/* Get status / file type from user */
status = get_status();
/* Get tax results. */
income_adj = get_income_adjusted(
income_dep,
status
);
/* Print final data. */
print_result(
wages,
income_other,
interests,
dividends,
income_dep,
income_adj
);
/* Return status / type for statistics in main(). */
return status;
}
int main(void)
{
int loop,
filing, /* Temporary filing value. */
filings[] = {0, 0, 0, 0}; /* Counter for filings. */
loop = confirm("Should we start? ");
while (loop) {
/* Process one filing and set "filing" to status/type returned
* by process_filing().*/
filing = process_filing();
/* Increment index for statistics. */
filings[filing] += 1;
/* Keep asking for more */
loop = confirm("Would you like to file another? ");
}
/* Print final statistics. */
print_filing_stats(
filings[0],
filings[1],
filings[2],
filings[3]
);
/* Print message instead of only pausing, as that could be interpreted
* as application hang / crash at exit. */
fprintf(stderr, "\nPress enter to quit. ");
getchar();
return 0;
}