]>
git.scottworley.com Git - inverse-tax/blob - tax.js
3 function near(a
, b
, epsilon
= 1e-6) {
4 return Math
.abs(a
- b
) < epsilon
;
7 function less_than_or_near(a
, b
, epsilon
= 1e-6) {
8 return a
< b
|| near(a
, b
, epsilon
);
11 function parse_table(as_text
) {
12 function parse_line(line
) {
13 return line
.trim().split(' ').filter(x
=> x
!== '').map(x
=> parseFloat(x
));
15 return as_text
.trim().split('\n').map(parse_line
);
18 function tax_table_from_table(table
) {
19 return table
.map(([start
, rate
], i
, table
) =>
20 [start
, i
< table
.length
- 1 ? table
[i
+1][0] : Infinity
, rate
/ 100.0]);
23 function parse_tax_table(as_text
) {
24 return tax_table_from_table(parse_table(as_text
));
28 return nums
.reduce((total
, num
) => total
+ num
, 0);
31 function tax(table
, income
) {
32 return sum(table
.map(([start
, end
, rate
]) =>
33 Math
.max(0, Math
.min(income
, end
) - start
) * rate
));
36 function apply_deductible(table
, deductible
) {
37 return table
.map(([start
, end
, rate
]) => [start
+ deductible
, end
+ deductible
, rate
]);
40 function merge_tax_tables(t1
, t2
) {
41 if (t1
.length
== 0) return t2
;
42 if (t2
.length
== 0) return t1
;
43 const [start1
, end1
, rate1
] = t1
[0];
44 const [start2
, end2
, rate2
] = t2
[0];
45 if (start1
== start2
) {
47 return [[start1
, end1
, rate1
+ rate2
]].concat(merge_tax_tables(t1
.slice(1), t2
.slice(1)));
50 return [[start1
, end1
, rate1
+ rate2
]].concat(merge_tax_tables(t1
.slice(1), [[end1
, end2
, rate2
]].concat(t2
.slice(1))));
52 return merge_tax_tables(t2
, t1
);
54 if (start1
< start2
) {
56 return [t1
[0]].concat(merge_tax_tables(t1
.slice(1), t2
));
58 return [[start1
, start2
, rate1
]].concat(merge_tax_tables([[start2
, end1
, rate1
]].concat(t1
.slice(1)), t2
));
60 return merge_tax_tables(t2
, t1
);
63 function invert(table
) {
65 // net = m * gross + b
67 // net - b = m * gross
68 // (net - b) / m = gross
70 const ms
= table
.map(([start
, end
, rate
]) => 1 - rate
);
71 const full_brackets
= [0].concat(table
.map(([start
, end
, rate
]) => (end
- start
) * rate
)).slice(0, table
.length
);
72 function sum_lower_brackets(remaining_brackets
, acc
= 0) {
73 if (remaining_brackets
.length
== 0) return [];
74 return [acc
+ remaining_brackets
[0]].concat(sum_lower_brackets(remaining_brackets
.slice(1), acc
+ remaining_brackets
[0]));
76 const bs
= sum_lower_brackets(full_brackets
).map((lower_brackets
, i
) => {
77 const [start
, end
, rate
] = table
[i
];
79 // net = gross - lower_brackets - rate * (gross - start)
80 // net = gross - lower_brackets - rate * gross + rate * start
81 // net = gross - rate * gross - lower_brackets + rate * start
82 // net = (1 - rate) * gross - lower_brackets + rate * start
83 // net = m * gross - lower_brackets + rate * start
84 // \_____________________________/ - here is b
85 return rate
* start
- lower_brackets
;
87 const inverse_table
= table
.map(([start
, end
, rate
], i
) => {
90 return [m
* start
+ b
, m
* end
+ b
, m
, b
];
92 return function(net
) {
93 for (const [start
, end
, m
, b
] of inverse_table
) {
94 if (less_than_or_near(start
, net
) && less_than_or_near(net
, end
)) {