program define taxpuf
version 8.2
/*
** by Jean Roth , jroth@nber.org
This program uses the NBER TAXSIM model to calculate US and state
income tax liabilities.
data1-data255 description http://www.nber.org/taxsim/pctaxsim.ndx
*/

display "begin taxpuf.ado"
syntax [, Secondary Wages Interest Output(string) Temp]
preserve
**  Default:  Do _not_ write permanent copies of the ASCII input and output files
if `"`temp'"' != `"temp"' {
     tempfile tdata tmpresult
}
else {
     display "Saving input to FORTRAN TAXSIM as `output'tdata.asc "
     local tdata `output'tdata.asc
     display "Saving output from FORTRAN TAXSIM as `output'tmpresult.asc "
     local tmpresult `output'tmpresult.asc
}

! /bin/rm -f tmpresult.asc tdata.asc
**  Default:  Write ./taxsim_out.dta to current directory
if length(`"`output'"') == 0 {
     local output taxsim_out
}
display "TAXSIM results will be saved in `output'.dta"
*capture drop taxsimid
*generate taxsimid = _n
*replace data100 = taxsimid
generate int mtr  = 85
generate int idtl = 2
display "Full Intermediate Calculations Requested"
**  Default:  mtr == 85, Marginal rate with respect to primary earner
if `"`secondary'"' == `"secondary"' {
     quietly replace mtr = 86
     display "Marginal rate with respect to secondary earner"
}
if `"`wages'"' == `"wages"' {
     quietly replace mtr = 11
     display "Marginal rate with respect to overall earnings"
}
if `"`interest'"' == `"interest"' {
     quietly replace mtr = 14
     display "Marginal rate with respect to interest income"
}
if mtr[1] == 85 {
     display "Marginal rate with respect to primary earner"
}


** Check data sent to taxsim for validity
** Checking data set for observations
capture assert _N > 0
if _rc > 0 {
  display "Aborting:  No observations in data set"
  exit
}
** taxpuf seems to be just from primary earner at this time
**
** Stata's default behavior which cannot be turned off is to unabbreviate
** variable names.  This means Stata can interpret a "drop if data6 < 0" command
** in one of four ways:
** Case 1:  data6 exists.  Correctly drop data6.
** Case 2:  data6 does not exist and there is exactly one unabbreviated
**          variable.  Incorrectly drop the unabreviated variable.
** Case 3:  data6 does not exist and multiple unabbreviated variable names
** Case 4:  No data6* variables exist.  Fail with a "not found" message.
** In the loop below, the varexist program called below creates flags
** to indicate the exisistence of data1-data255.  If a data1-data255
** variable does not exist, it is created as a zero-valued variable.
** Therefore, any subsequent commands such as "drop data6" will act
** only on the intended variable.
forvalues X = 1/255 {
     varexist `X'
     scalar exists`X' = r(exists)
     ** Make zero-valued data`X' variables if they do not exist
     if exists`X' == 0 {
          gen data`X' = 0
     }
}

local must_exist 103 2 7
foreach X of local must_exist {
     if exists`X' == 0 {
          display "Aborting:  Variable data`X' not found in data file."
          exit
     }
     capture sum(data`X')
     if ( r(min) ==  0 & r(max) == 0 ) {
          display "Aborting:  All values of variable data`X' are zero."
          exit
     }
}
scalar nstart = _N
display nstart " initial observation(s)"
** Clean data103
quietly drop if data103<1960 | data103>2013 | data103==.
scalar n103 = _N
scalar m103 = nstart - _N
if m103 display "Dropping " m103 " observations with out of range data103 values"
** Clean data6
quietly drop if data6<0 | data6 > 51 | missing(data6)
** The maximum value of data103 will need updating annually ;
quietly drop if (data103<1977|data103>2004) & missing(data6) != 1 & data6 !=0
scalar n6 = _N
scalar m6 = n103 - _N
if m6 display "Dropping " m6 " observations with out of range data6 values"
** Clean data8
quietly drop if data8<0 | data8>999
scalar n8 = _N
scalar m8 = n6 - _N
if m8 display "Dropping " m8 " observations with out of range data8 values"
*quietly drop if data2<1 | (data2>3 & data2<7) | data2>7
quietly drop if data2<1 | data2>7
scalar n2 = _N
scalar m2 = n8 - _N
if m2 display "Dropping " m2 " observations with out of range data2 values"
** Clean data9
quietly drop if data9<0 | data9>2
scalar n9 = _N
scalar m9 = n2 - _N
if m9 display "Dropping " m9 " observations with out of range data9 values"
gen id = _n
replace data252 = idtl
replace data253 = mtr
** Put variables in alphabetic order so that data1 and data2 are first, not last, etc.
aorder
outfile data* using `tdata', noquote comma
display "Sending " _N " observations to taxpuf "
! /usr/local/bin/taxpuf <`tdata'   >`tmpresult'
!tail -4 `tmpresult'
local results data100  data103 state fiitax siitax fica frate srate ficar v10-v41
infile  `results' using  `tmpresult'  ,clear
label variable fiitax "Federal Income Tax"
label variable siitax "Stata Income Tax"
label variable fica   "OASDI and HI Payroll Tax"
label variable frate  "IIT marginal rate"
label variable srate  "state marginal rate"
label variable ficar  "SS marginal rate"
label variable v10 "Federal AGI"
label variable v11 "UI in AGI"
label variable v12 "Social Security in AGI"
label variable v13 "Zero Bracket Amount"
label variable v14 "Personal Exemptions"
label variable v15 "Exemption Phaseout"
label variable v16 "Deduction Phaseout"
label variable v17 "Deductions allowed"
label variable v18 "Federal Taxable Income"
label variable v19 "Federal Regular Tax"
label variable v20 "Exemption Surtax"
label variable v21 "General Tax Credit"
label variable v22 "Child Tax Credit (as adjusted)"
label variable v23 "Refundable Part of Child Tax Credit"
label variable v24 "Child Care Credit"
label variable v25 "Earned Income Credit"
label variable v26 "Income for the Alternative Minimum Tax"
label variable v27 "AMT Liability (addition to regular tax)"
label variable v28 "Income Tax After Credits"
label variable v29 "FICA"
label variable v30 "State Household Income"
label variable v31 "State Rent Payments"
label variable v32 "State AGI"
label variable v33 "State Exemption amount"
label variable v34 "State Standard Deduction"
label variable v35 "State Itemized Deductions"
label variable v36 "State Taxable Income"
label variable v37 "State Property Tax Credit"
label variable v38 "State Child Care Credit"
label variable v39 "State General Credit "
label variable v40 "State Total Credits"
label variable v41 "State Bracket Rate"
sum
sort data100 data103
save `output', replace
restore
display " "
display "TAXSIM results are saved in `output'"
display " "

end

program define varexist , rclass
version 8.2
** by Jean Roth , jroth@nber.org
** This program returns scalar dummy variables
** exists`X' that indicate the existence of data`X' for data1-data255.
local varname data`1'
**display "Q.  Does `varname' exist? "
capture confirm var `varname'
if _rc == 0 {
     ** Cases 1, 2, and 3
     **display "A:  Maybe."
     unab this_varlist : `varname'
     **display "     Q:  Does `this_varlist' == `varname' ?"
     if "`this_varlist'" == "`varname'"  {
          ** Case 1
          **display "     A:  Yes, `varname' exists "
          return scalar exists = 1
     }
     else {
          ** Case 2 or Case 3
          display "     A:  No, `this_varlist' != `varname' "
          return scalar exists = 0
     }
}
else {
     ** Case 4.
     **display "A.  No, `varname' does not exist "
     return scalar exists = 0
}
end