Payroll::US::FedIncome

calculate(
   gross - total amount of pay
   date - date to be paid on.  affects tax rates  format YYYYMMDD
   method - the method to use for calculation (currently only percentage )
   allowances - federal allowances
   period - annual, semiannual, quarterly, monthly, semimonthly, biweekly, weekly, daily
   marital - single or married
   periodDays - reference to a hash containing ratios for number of days in periods. defaults to undef, hash can be overriden by the user specifying a hash ref to the same structure, different data.
  round - yes, no - defaults to yes - or should user sprintf the result.  seems that would be more efficient
)

The data will be stored by date in a hash, so that calculations will use the proper rates.

iterate over sorted keys descending in data hash.  
  Compare $date to keys.  
  Stop when $date >= key.  
  set $foundDate = $key.
  If date < all keys, that is an error.  

data{$foundDate}->
{tables}

ex. data{20010101}->
{dailyWithholdingAllowance}
= 11.15

{dailyTableRows}[X]


The periodDays hash holds the number of days in a period
periodDays->
{annual}
= 260

{semiannual}
= 130

{quarterly}
= 65

{monthly}
= 21.67

{semimonthly}
= 10.84

{biweekly}
= 10

{weekly}
= 5

{daily}
= 1

To calculate the withholding allowance
withholdingAllowance = periodDays->{$period} * data->{$foundDate}->{dailyWithholdingAllowance}
modifiedGross = gross - (allowances * withholdingAllowance)

Use modifiedGross value to determine tax bracket and subsequent percentage by looking up in data{$foundDate}->{tables}.  Only generate withholding tables as needed.  This will allow for caching type functionality and speed optimizations.

How to generate a table.

generate_table ( period, marital, table=>data{$foundDate}->{tables} )
{
   @dailyTableRows = $data->{$foundDate}->{$dailyTableRows}
   for $row = 0; row < scalar ($dailyTableRows); row++
   {
      if $row == 0
         $base = 0;
     else
     {
        base = $dailyTableRows[row -1]->percent *
             ($dailyTableRows[row]->top - $dailyTableRows[row -1]->top)
             + $dailyTableRows[row-1]->base
     }
     $top = $row->{top} * periodDays{period}
     $top *= $row->{marriedRatio} if $marital eq "married"

     %tempRow = { top => $top,
        base => base ,  
        percent => $row->{percent} } ;
     #dont forget to create array single first       
     push($table->{$period}->{$marital}, \%tempRow)
  }
}

The lookuptables are generated from the data stored in the daily table.  
There is an array for the rows sorted descending.  This allows us to check the greatest case first.  No corner cases.
{dailyTableRows}
[$row]->
{top}


{percent}


{marriedRatio}

generated tables look like this

tables
{$period =>
{ single =>
[



{top => , base => , percent => } ,



] ,


married =>
[



{top => , base => , percent => } ,



]


},


}




these variables will be lookups from the above generated table.
if !tableexists
  generate_table(period, marital, table =>)
tax = $base + (( modifiedGross - $top)  * $percent)