Download
(defvar 1040tt-tax-brackets
'((single
(379150 . 0.35)
(174400 . 0.33)
( 83600 . 0.28)
( 34500 . 0.25)
( 8500 . 0.15)
( 0 . 0.1))
(filing-jointly
(379150 . 0.35)
(212300 . 0.33)
(139350 . 0.28)
( 69000 . 0.25)
( 17000 . 0.15)
( 0 . 0.1))
(filing-separately
(189575 . 0.35)
(106150 . 0.33)
( 69675 . 0.28)
( 34500 . 0.25)
( 8500 . 0.15)
( 0 . 0.1))
(head-of-household
(379150 . 0.35)
(193350 . 0.33)
(119400 . 0.28)
( 46250 . 0.25)
( 12150 . 0.15)
( 0 . 0.1)))
"Tax schedule for 2012 form 1040.")
(defvar 1040tt-use-dynamic-programming nil
"If non-nil compute with dynamic programming heuristic.
Dynamic programming avoids computing values that have already
been determined by storing the associations.")
(defvar 1040tt-tax-hash nil
"Hash table used for dynamic programming algorithm.
See `1040tt-use-dynamic-programming'.")
(defconst 1040tt-tax-prompt "Taxable income: "
"Prompt string for taxable income.")
(defun 1040tt-income-tax (income status)
"Compute tax for INCOME when filing as STATUS.
If INCOME is in tax table, return tax for its entry.
Otherwise, compute value using"
(interactive
(let ((i (read-number 1040tt-tax-prompt))
(s (1040tt-read-filing-status)))
(list i s)))
(let* ((line (1040tt-tax-line-for income))
(tax (if (null line)
(1040tt-income-tax-estimate income status)
(1040tt-round
(1040tt-income-tax-estimate line status)))))
(message (format "You owe $%.2f in income tax" tax))
tax))
(defun 1040tt-read-filing-status ()
"Read filing status from minibuffer.
A status must be caar value of `1040tt-tax-brackets'."
(intern
(completing-read "Filing status: "
(mapcar
'symbol-name
(mapcar 'car
1040tt-tax-brackets))
nil 'require-match)))
(defun 1040tt-tax-line-for (income)
"Determine line in tax table for INCOME.
If entry doesn't exist for INCOME, then return INCOME."
(let ((range (1040tt-tax-range income))
(offset (cond ((< income 5.0) 0)
((< income 25.0) 5)
(t 0))))
(if (null range)
nil
(+ offset (/ range 2.0)
(* (/ (truncate income) (+ offset range))
range)))))
(defun 1040tt-tax-range (income)
"Return span of row in tax table for INCOME."
(cond ((< income 5.0) 5)
((< income 25.0) 10)
((< income 3000.0) 25)
((< income 100000.0) 50)
(t nil)))
(defun 1040tt-round (n)
"Round to nearest integer of N.
If equidistant between integers, round up.
For example, (1040tt-round 2.5) returns 3, never 2.
Emacs's `round' is system-dependent."
(+ (truncate n)
(1- (round (1+ (mod n 1))))))
(defun 1040tt-income-tax-estimate (income status)
"Compute tax for INCOME when filing as STATUS without tax tables.
For finding the tax with tax tables where necessary, use
the `1040tt-income-tax' command."
(interactive
(let ((i (read-number 1040tt-tax-prompt))
(s (1040tt-read-filing-status)))
(list i s)))
(let ((brackets (assoc status 1040tt-tax-brackets)))
(if (null brackets)
(error "Unknown filing status %s" (symbol-name status))
(let ((tax (1040tt-income-tax-estimate- income status (cdr brackets))))
(message (format "Your estimated income tax is $%.2f" tax))
tax))))
(defun 1040tt-income-tax-estimate- (income status brackets)
"Calculate tax for INCOME filing as STATUS using BRACKETS.
Income tax is calculated by adding together the dollars taxed in
each tax bracket. It is not done by multiply a taxable income by
the rate for its bracket."
(if (or (< income 0) (null brackets))
0
(when 1040tt-use-dynamic-programming
(if (null 1040tt-tax-hash)
(setq 1040tt-tax-hash (make-hash-table)))
(if (null (gethash status 1040tt-tax-hash))
(puthash status (make-hash-table) 1040tt-tax-hash)))
(let* ((income-level (caar brackets))
(tax-rate (cdar brackets))
(tax
(if (and 1040tt-use-dynamic-programming
(numberp (gethash income
(gethash status
1040tt-tax-hash))))
(gethash income (gethash status 1040tt-tax-hash))
(if (> income income-level)
(+ (* (- income income-level) tax-rate)
(1040tt-income-tax-estimate- income-level status
(cdr brackets)))
(1040tt-income-tax-estimate- income status
(cdr brackets))))))
(if (not 1040tt-use-dynamic-programming)
tax
(puthash income tax (gethash status 1040tt-tax-hash))))))
(defun 1040tt-tax-worksheet (income status)
"Compute tax with 1040tt worksheet for INCOME when filing as STATUS."
(let* ((rate (or (1040tt-tax-rate income status)
0))
(income-level (or (1040tt-tax-level income status)
0))
(tax (- (* income rate)
(1040tt-income-tax-estimate income status))))
tax))
(defun 1040tt-tax-reduction (income status)
"Compute reduction in worksheet for INCOME when filing as STATUS.
See `1040tt-tax-worksheet'."
(let* ((rate (1040tt-tax-rate income status))
(tax (1040tt-income-tax-estimate income status)))
(if (null rate)
0
(let ((reduction (- (* rate income)
tax)))
reduction))))
(defun 1040tt-tax-level (income status)
"Display income level for INCOME when filing as STATUS."
(interactive
(let ((i (read-number 1040tt-tax-prompt))
(s (1040tt-read-filing-status)))
(list i s)))
(let ((brackets (assoc status 1040tt-tax-brackets)))
(if (null brackets)
(error "Unknown filing status %s" (symbol-name status))
(let* ((levels (progn (delq nil
(mapcar (lambda (b)
(and (> income (car b)) (car b)))
(cdr brackets)))))
(level (if (> (length levels) 0)
(apply 'max levels)
levels)))
(if (null level)
(message "No income tax level found")
(message "Your income tax level is $%d" level))
level))))
(defun 1040tt-tax-rate (income status)
"Display tax rate for INCOME when filing as STATUS."
(interactive
(let ((i (read-number 1040tt-tax-prompt))
(s (1040tt-read-filing-status)))
(list i s)))
(let ((brackets (assoc status 1040tt-tax-brackets)))
(if (null brackets)
(error "Unknown filing status %s" (symbol-name status))
(let* ((level (1040tt-tax-level income status))
(rate (cdr (assoc level brackets))))
(if (null rate)
(message "No income tax rate found")
(message "Your income tax rate is %d%% (%.2f%%)" (* 100.0 rate)
(* 100
(/ (1040tt-income-tax-estimate income status)
income))))
rate))))
(defun 1040tt-insert-tax-table ()
"Insert Form 1040tt tax table."
(interactive "*")
(let ((rows (vconcat [0 5 15]
(number-sequence 25 2975 25)
(number-sequence 3000 99950 50)))
(statuses (mapcar 'car 1040tt-tax-brackets))
(1040tt-use-dynamic-programming t))
(mapc
(lambda (r)
(insert (number-to-string r)
"\t"
(number-to-string (+ r (or (1040tt-tax-range r)
0)))
"\t")
(insert
(mapconcat 'number-to-string
(mapcar (lambda (s)
(1040tt-income-tax r s))
statuses)
"\t"))
(newline))
rows)))
(provide '1040tt)