kumi 0.0.25 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +70 -71
  4. data/data/functions/agg/boolean.yaml +6 -2
  5. data/data/functions/agg/numeric.yaml +32 -16
  6. data/data/functions/agg/string.yaml +4 -3
  7. data/data/functions/core/arithmetic.yaml +62 -14
  8. data/data/functions/core/boolean.yaml +12 -6
  9. data/data/functions/core/comparison.yaml +25 -13
  10. data/data/functions/core/constructor.yaml +16 -8
  11. data/data/functions/core/select.yaml +3 -1
  12. data/data/functions/core/stencil.yaml +14 -5
  13. data/data/functions/core/string.yaml +9 -4
  14. data/data/kernels/ruby/agg/numeric.yaml +1 -1
  15. data/docs/UNSAT_DETECTION.md +83 -0
  16. data/golden/array_element/expected/nast.txt +1 -1
  17. data/golden/array_element/expected/schema_ruby.rb +1 -1
  18. data/golden/array_index/expected/nast.txt +7 -7
  19. data/golden/array_index/expected/schema_ruby.rb +1 -1
  20. data/golden/array_operations/expected/nast.txt +2 -2
  21. data/golden/array_operations/expected/schema_ruby.rb +1 -1
  22. data/golden/array_operations/expected/snast.txt +3 -3
  23. data/golden/cascade_logic/expected/schema_ruby.rb +1 -1
  24. data/golden/cascade_logic/expected/snast.txt +2 -2
  25. data/golden/chained_fusion/expected/nast.txt +2 -2
  26. data/golden/chained_fusion/expected/schema_ruby.rb +1 -1
  27. data/golden/element_arrays/expected/nast.txt +2 -2
  28. data/golden/element_arrays/expected/schema_ruby.rb +1 -1
  29. data/golden/element_arrays/expected/snast.txt +1 -1
  30. data/golden/empty_and_null_inputs/expected/nast.txt +3 -3
  31. data/golden/empty_and_null_inputs/expected/schema_ruby.rb +1 -1
  32. data/golden/function_overload/expected/ast.txt +29 -0
  33. data/golden/function_overload/expected/input_plan.txt +4 -0
  34. data/golden/function_overload/expected/lir_00_unoptimized.txt +18 -0
  35. data/golden/function_overload/expected/lir_01_hoist_scalar_references.txt +18 -0
  36. data/golden/function_overload/expected/lir_02_inlined.txt +20 -0
  37. data/golden/function_overload/expected/lir_03_cse.txt +20 -0
  38. data/golden/function_overload/expected/lir_04_1_loop_fusion.txt +20 -0
  39. data/golden/function_overload/expected/lir_04_loop_invcm.txt +20 -0
  40. data/golden/function_overload/expected/lir_06_const_prop.txt +20 -0
  41. data/golden/function_overload/expected/nast.txt +22 -0
  42. data/golden/function_overload/expected/schema_javascript.mjs +12 -0
  43. data/golden/function_overload/expected/schema_ruby.rb +39 -0
  44. data/golden/function_overload/expected/snast.txt +22 -0
  45. data/golden/function_overload/input.json +8 -0
  46. data/golden/function_overload/schema.kumi +19 -0
  47. data/golden/game_of_life/expected/lir_00_unoptimized.txt +4 -4
  48. data/golden/game_of_life/expected/lir_01_hoist_scalar_references.txt +4 -4
  49. data/golden/game_of_life/expected/lir_02_inlined.txt +16 -16
  50. data/golden/game_of_life/expected/lir_03_cse.txt +20 -16
  51. data/golden/game_of_life/expected/lir_04_1_loop_fusion.txt +20 -16
  52. data/golden/game_of_life/expected/lir_04_loop_invcm.txt +20 -16
  53. data/golden/game_of_life/expected/lir_06_const_prop.txt +20 -16
  54. data/golden/game_of_life/expected/nast.txt +4 -4
  55. data/golden/game_of_life/expected/schema_javascript.mjs +4 -2
  56. data/golden/game_of_life/expected/schema_ruby.rb +5 -3
  57. data/golden/game_of_life/expected/snast.txt +10 -10
  58. data/golden/hash_keys/expected/schema_ruby.rb +1 -1
  59. data/golden/hash_value/expected/nast.txt +1 -1
  60. data/golden/hash_value/expected/schema_ruby.rb +1 -1
  61. data/golden/hash_value/expected/snast.txt +1 -1
  62. data/golden/hierarchical_complex/expected/nast.txt +3 -3
  63. data/golden/hierarchical_complex/expected/schema_ruby.rb +1 -1
  64. data/golden/hierarchical_complex/expected/snast.txt +3 -3
  65. data/golden/inline_rename_scope_leak/expected/nast.txt +3 -3
  66. data/golden/inline_rename_scope_leak/expected/schema_ruby.rb +1 -1
  67. data/golden/input_reference/expected/nast.txt +2 -2
  68. data/golden/input_reference/expected/schema_ruby.rb +1 -1
  69. data/golden/interleaved_fusion/expected/nast.txt +2 -2
  70. data/golden/interleaved_fusion/expected/schema_ruby.rb +1 -1
  71. data/golden/let_inline/expected/nast.txt +4 -4
  72. data/golden/let_inline/expected/schema_ruby.rb +1 -1
  73. data/golden/loop_fusion/expected/nast.txt +1 -1
  74. data/golden/loop_fusion/expected/schema_ruby.rb +1 -1
  75. data/golden/min_reduce_scope/expected/nast.txt +3 -3
  76. data/golden/min_reduce_scope/expected/schema_ruby.rb +1 -1
  77. data/golden/min_reduce_scope/expected/snast.txt +1 -1
  78. data/golden/mixed_dimensions/expected/nast.txt +2 -2
  79. data/golden/mixed_dimensions/expected/schema_ruby.rb +1 -1
  80. data/golden/multirank_hoisting/expected/nast.txt +7 -7
  81. data/golden/multirank_hoisting/expected/schema_ruby.rb +1 -1
  82. data/golden/nested_hash/expected/nast.txt +1 -1
  83. data/golden/nested_hash/expected/schema_ruby.rb +1 -1
  84. data/golden/reduction_broadcast/expected/nast.txt +3 -3
  85. data/golden/reduction_broadcast/expected/schema_ruby.rb +1 -1
  86. data/golden/reduction_broadcast/expected/snast.txt +1 -1
  87. data/golden/roll/expected/schema_ruby.rb +1 -1
  88. data/golden/shift/expected/schema_ruby.rb +1 -1
  89. data/golden/shift_2d/expected/schema_ruby.rb +1 -1
  90. data/golden/simple_math/expected/lir_00_unoptimized.txt +1 -1
  91. data/golden/simple_math/expected/lir_01_hoist_scalar_references.txt +1 -1
  92. data/golden/simple_math/expected/lir_02_inlined.txt +1 -1
  93. data/golden/simple_math/expected/lir_03_cse.txt +1 -1
  94. data/golden/simple_math/expected/lir_04_1_loop_fusion.txt +1 -1
  95. data/golden/simple_math/expected/lir_04_loop_invcm.txt +1 -1
  96. data/golden/simple_math/expected/lir_06_const_prop.txt +1 -1
  97. data/golden/simple_math/expected/nast.txt +5 -5
  98. data/golden/simple_math/expected/schema_ruby.rb +1 -1
  99. data/golden/simple_math/expected/snast.txt +2 -2
  100. data/golden/streaming_basics/expected/nast.txt +8 -8
  101. data/golden/streaming_basics/expected/schema_ruby.rb +1 -1
  102. data/golden/streaming_basics/expected/snast.txt +1 -1
  103. data/golden/tuples/expected/lir_00_unoptimized.txt +5 -5
  104. data/golden/tuples/expected/lir_01_hoist_scalar_references.txt +5 -5
  105. data/golden/tuples/expected/lir_02_inlined.txt +5 -5
  106. data/golden/tuples/expected/lir_03_cse.txt +5 -5
  107. data/golden/tuples/expected/lir_04_1_loop_fusion.txt +5 -5
  108. data/golden/tuples/expected/lir_04_loop_invcm.txt +5 -5
  109. data/golden/tuples/expected/lir_06_const_prop.txt +5 -5
  110. data/golden/tuples/expected/nast.txt +4 -4
  111. data/golden/tuples/expected/schema_ruby.rb +1 -1
  112. data/golden/tuples/expected/snast.txt +6 -6
  113. data/golden/tuples_and_arrays/expected/lir_00_unoptimized.txt +1 -1
  114. data/golden/tuples_and_arrays/expected/lir_01_hoist_scalar_references.txt +1 -1
  115. data/golden/tuples_and_arrays/expected/lir_02_inlined.txt +2 -2
  116. data/golden/tuples_and_arrays/expected/lir_03_cse.txt +2 -2
  117. data/golden/tuples_and_arrays/expected/lir_04_1_loop_fusion.txt +2 -2
  118. data/golden/tuples_and_arrays/expected/lir_04_loop_invcm.txt +2 -2
  119. data/golden/tuples_and_arrays/expected/lir_06_const_prop.txt +2 -2
  120. data/golden/tuples_and_arrays/expected/nast.txt +3 -3
  121. data/golden/tuples_and_arrays/expected/schema_ruby.rb +1 -1
  122. data/golden/tuples_and_arrays/expected/snast.txt +2 -2
  123. data/golden/us_tax_2024/expected/ast.txt +63 -670
  124. data/golden/us_tax_2024/expected/input_plan.txt +8 -45
  125. data/golden/us_tax_2024/expected/lir_00_unoptimized.txt +253 -863
  126. data/golden/us_tax_2024/expected/lir_01_hoist_scalar_references.txt +253 -863
  127. data/golden/us_tax_2024/expected/lir_02_inlined.txt +1215 -5139
  128. data/golden/us_tax_2024/expected/lir_03_cse.txt +587 -2460
  129. data/golden/us_tax_2024/expected/lir_04_1_loop_fusion.txt +632 -2480
  130. data/golden/us_tax_2024/expected/lir_04_loop_invcm.txt +587 -2460
  131. data/golden/us_tax_2024/expected/lir_06_const_prop.txt +587 -2460
  132. data/golden/us_tax_2024/expected/nast.txt +123 -826
  133. data/golden/us_tax_2024/expected/schema_javascript.mjs +127 -581
  134. data/golden/us_tax_2024/expected/schema_ruby.rb +135 -610
  135. data/golden/us_tax_2024/expected/snast.txt +155 -858
  136. data/golden/us_tax_2024/expected.json +120 -1
  137. data/golden/us_tax_2024/input.json +18 -9
  138. data/golden/us_tax_2024/schema.kumi +48 -178
  139. data/golden/with_constants/expected/lir_00_unoptimized.txt +1 -1
  140. data/golden/with_constants/expected/lir_01_hoist_scalar_references.txt +1 -1
  141. data/golden/with_constants/expected/lir_02_inlined.txt +1 -1
  142. data/golden/with_constants/expected/lir_03_cse.txt +1 -1
  143. data/golden/with_constants/expected/lir_04_1_loop_fusion.txt +1 -1
  144. data/golden/with_constants/expected/lir_04_loop_invcm.txt +1 -1
  145. data/golden/with_constants/expected/lir_06_const_prop.txt +1 -1
  146. data/golden/with_constants/expected/nast.txt +2 -2
  147. data/golden/with_constants/expected/schema_ruby.rb +1 -1
  148. data/golden/with_constants/expected/snast.txt +2 -2
  149. data/lib/kumi/analyzer.rb +12 -12
  150. data/lib/kumi/core/analyzer/passes/formal_constraint_propagator.rb +236 -0
  151. data/lib/kumi/core/analyzer/passes/input_collector.rb +22 -4
  152. data/lib/kumi/core/analyzer/passes/nast_dimensional_analyzer_pass.rb +64 -18
  153. data/lib/kumi/core/analyzer/passes/normalize_to_nast_pass.rb +9 -4
  154. data/lib/kumi/core/analyzer/passes/snast_pass.rb +3 -1
  155. data/lib/kumi/core/analyzer/passes/unsat_detector.rb +172 -198
  156. data/lib/kumi/core/error_reporter.rb +36 -1
  157. data/lib/kumi/core/errors.rb +33 -1
  158. data/lib/kumi/core/functions/function_spec.rb +5 -4
  159. data/lib/kumi/core/functions/loader.rb +17 -1
  160. data/lib/kumi/core/functions/overload_resolver.rb +164 -0
  161. data/lib/kumi/core/functions/type_error_reporter.rb +118 -0
  162. data/lib/kumi/core/functions/type_rules.rb +155 -35
  163. data/lib/kumi/core/types/inference.rb +29 -22
  164. data/lib/kumi/core/types/normalizer.rb +29 -45
  165. data/lib/kumi/core/types/validator.rb +16 -27
  166. data/lib/kumi/core/types/value_objects.rb +116 -0
  167. data/lib/kumi/core/types.rb +45 -37
  168. data/lib/kumi/registry_v2/loader.rb +90 -0
  169. data/lib/kumi/registry_v2.rb +18 -1
  170. data/lib/kumi/version.rb +1 -1
  171. metadata +21 -7
  172. data/lib/kumi/core/analyzer/unsat_constant_evaluator.rb +0 -59
  173. data/lib/kumi/core/atom_unsat_solver.rb +0 -396
  174. data/lib/kumi/core/constraint_relationship_solver.rb +0 -641
  175. data/lib/kumi/core/types/builder.rb +0 -23
  176. data/lib/kumi/core/types/compatibility.rb +0 -96
  177. data/lib/kumi/core/types/formatter.rb +0 -26
@@ -1 +1,120 @@
1
- {"summary": {"single": {"federal": {"marginal": 0.22, "effective": 0.13841, "tax": 13841.0}, "fica": {"effective": 0.0765, "tax": 7650.0}, "state": {"marginal": 0.06, "effective": 0.06, "tax": 6000.0}, "local": {"marginal": 0.0388, "effective": 0.0388, "tax": 3880.0}, "total": {"effective": 0.31371, "tax": 31371.0}, "after_tax": 68629.0, "retirement_contrib": 0, "take_home": 68629.0}, "married_joint": {"federal": {"marginal": 0.12, "effective": 0.08032, "tax": 8032.0}, "fica": {"effective": 0.0765, "tax": 7650.0}, "state": {"marginal": 0.06, "effective": 0.06, "tax": 6000.0}, "local": {"marginal": 0.0388, "effective": 0.0388, "tax": 3880.0}, "total": {"effective": 0.25562, "tax": 25562.0}, "after_tax": 74438.0, "retirement_contrib": 0, "take_home": 74438.0}, "married_separate": {"federal": {"marginal": 0.22, "effective": 0.13841, "tax": 13841.0}, "fica": {"effective": 0.0765, "tax": 7650.0}, "state": {"marginal": 0.06, "effective": 0.06, "tax": 6000.0}, "local": {"marginal": 0.0388, "effective": 0.0388, "tax": 3880.0}, "total": {"effective": 0.31371, "tax": 31371.0}, "after_tax": 68629.0, "retirement_contrib": 0, "take_home": 68629.0}, "head_of_household": {"federal": {"marginal": 0.22, "effective": 0.10541, "tax": 10541.0}, "fica": {"effective": 0.0765, "tax": 7650.0}, "state": {"marginal": 0.06, "effective": 0.06, "tax": 6000.0}, "local": {"marginal": 0.0388, "effective": 0.0388, "tax": 3880.0}, "total": {"effective": 0.28071, "tax": 28071.0}, "after_tax": 71929.0, "retirement_contrib": 0, "take_home": 71929.0}}}
1
+ {
2
+ "summary": [
3
+ {
4
+ "filing_status": "single",
5
+ "federal": {
6
+ "marginal": 0.24,
7
+ "effective": 0.17025666666666667,
8
+ "tax": 25538.5
9
+ },
10
+ "fica": {
11
+ "effective": 0.0765,
12
+ "tax": 11475
13
+ },
14
+ "state": {
15
+ "marginal": 0,
16
+ "effective": 0,
17
+ "tax": 0
18
+ },
19
+ "local": {
20
+ "marginal": 0,
21
+ "effective": 0,
22
+ "tax": 0
23
+ },
24
+ "total": {
25
+ "effective": 0.24675666666666668,
26
+ "tax": 37013.5
27
+ },
28
+ "after_tax": 112986.5,
29
+ "retirement_contrib": 0,
30
+ "take_home": 112986.5
31
+ },
32
+ {
33
+ "filing_status": "married_joint",
34
+ "federal": {
35
+ "marginal": 0.22,
36
+ "effective": 0.11121333333333333,
37
+ "tax": 16682
38
+ },
39
+ "fica": {
40
+ "effective": 0.0765,
41
+ "tax": 11475
42
+ },
43
+ "state": {
44
+ "marginal": 0,
45
+ "effective": 0,
46
+ "tax": 0
47
+ },
48
+ "local": {
49
+ "marginal": 0,
50
+ "effective": 0,
51
+ "tax": 0
52
+ },
53
+ "total": {
54
+ "effective": 0.18771333333333334,
55
+ "tax": 28157
56
+ },
57
+ "after_tax": 121843,
58
+ "retirement_contrib": 0,
59
+ "take_home": 121843
60
+ },
61
+ {
62
+ "filing_status": "married_separate",
63
+ "federal": {
64
+ "marginal": 0.24,
65
+ "effective": 0.17025666666666667,
66
+ "tax": 25538.5
67
+ },
68
+ "fica": {
69
+ "effective": 0.078,
70
+ "tax": 11700
71
+ },
72
+ "state": {
73
+ "marginal": 0,
74
+ "effective": 0,
75
+ "tax": 0
76
+ },
77
+ "local": {
78
+ "marginal": 0,
79
+ "effective": 0,
80
+ "tax": 0
81
+ },
82
+ "total": {
83
+ "effective": 0.24825666666666665,
84
+ "tax": 37238.5
85
+ },
86
+ "after_tax": 112761.5,
87
+ "retirement_contrib": 0,
88
+ "take_home": 112761.5
89
+ },
90
+ {
91
+ "filing_status": "head_of_household",
92
+ "federal": {
93
+ "marginal": 0.24,
94
+ "effective": 0.14728666666666668,
95
+ "tax": 22093
96
+ },
97
+ "fica": {
98
+ "effective": 0.0765,
99
+ "tax": 11475
100
+ },
101
+ "state": {
102
+ "marginal": 0,
103
+ "effective": 0,
104
+ "tax": 0
105
+ },
106
+ "local": {
107
+ "marginal": 0,
108
+ "effective": 0,
109
+ "tax": 0
110
+ },
111
+ "total": {
112
+ "effective": 0.22378666666666666,
113
+ "tax": 33568
114
+ },
115
+ "after_tax": 116432,
116
+ "retirement_contrib": 0,
117
+ "take_home": 116432
118
+ }
119
+ ]
120
+ }
@@ -1,11 +1,14 @@
1
1
  {
2
- "income": 100000,
3
- "state_rate": 0.06,
4
- "local_rate": 0.0388,
2
+ "income": 150000,
3
+ "state_rate": 0,
4
+ "local_rate": 0,
5
5
  "retirement_contrib": 0,
6
- "fed": {
7
- "single": {
6
+ "filing_status": "single",
7
+ "statuses": [
8
+ {
9
+ "name": "single",
8
10
  "std": 14600,
11
+ "addl_threshold": 200000,
9
12
  "rates": [
10
13
  {
11
14
  "lo": 0,
@@ -44,8 +47,10 @@
44
47
  }
45
48
  ]
46
49
  },
47
- "married_joint": {
50
+ {
51
+ "name": "married_joint",
48
52
  "std": 29200,
53
+ "addl_threshold": 250000,
49
54
  "rates": [
50
55
  {
51
56
  "lo": 0,
@@ -84,8 +89,10 @@
84
89
  }
85
90
  ]
86
91
  },
87
- "married_separate": {
92
+ {
93
+ "name": "married_separate",
88
94
  "std": 14600,
95
+ "addl_threshold": 125000,
89
96
  "rates": [
90
97
  {
91
98
  "lo": 0,
@@ -124,8 +131,10 @@
124
131
  }
125
132
  ]
126
133
  },
127
- "head_of_household": {
134
+ {
135
+ "name": "head_of_household",
128
136
  "std": 21900,
137
+ "addl_threshold": 200000,
129
138
  "rates": [
130
139
  {
131
140
  "lo": 0,
@@ -164,5 +173,5 @@
164
173
  }
165
174
  ]
166
175
  }
167
- }
176
+ ]
168
177
  }
@@ -1,14 +1,17 @@
1
1
  schema do
2
2
  input do
3
- float :income
4
- float :state_rate
5
- float :local_rate
6
- float :retirement_contrib
7
-
8
- hash :fed do
9
- hash :single do
10
- float :std
11
- array :rates do
3
+ float :income
4
+ float :state_rate
5
+ float :local_rate
6
+ float :retirement_contrib
7
+ string :filing_status
8
+
9
+ array :statuses do
10
+ hash :status do
11
+ string :name
12
+ float :std
13
+ float :addl_threshold
14
+ array :rates do
12
15
  hash :bracket do
13
16
  float :lo
14
17
  float :hi # -1 = open-ended
@@ -16,40 +19,10 @@ schema do
16
19
  end
17
20
  end
18
21
  end
19
- hash :married_joint do
20
- float :std
21
- array :rates do
22
- hash :bracket do
23
- float :lo
24
- float :hi
25
- float :rate
26
- end
27
- end
28
- end
29
- hash :married_separate do
30
- float :std
31
- array :rates do
32
- hash :bracket do
33
- float :lo
34
- float :hi
35
- float :rate
36
- end
37
- end
38
- end
39
- hash :head_of_household do
40
- float :std
41
- array :rates do
42
- hash :bracket do
43
- float :lo
44
- float :hi
45
- float :rate
46
- end
47
- end
48
- end
49
22
  end
50
23
  end
51
24
 
52
- # ——— shared helpers ———
25
+ # shared
53
26
  let :big_hi, 100_000_000_000.0
54
27
  let :state_tax, input.income * input.state_rate
55
28
  let :local_tax, input.income * input.local_rate
@@ -60,144 +33,41 @@ schema do
60
33
  let :med_base_rate, 0.0145
61
34
  let :addl_med_rate, 0.009
62
35
 
63
- # thresholds differ by filing status
64
- let :addl_threshold_single, 200_000.0
65
- let :addl_threshold_mj, 250_000.0
66
- let :addl_threshold_ms, 125_000.0
67
- let :addl_threshold_hoh, 200_000.0
68
-
69
- # ——— SINGLE ———
70
- let :taxable_single, fn(:max, [input.income - input.fed.single.std, 0])
71
- let :lo_s, input.fed.single.rates.bracket.lo
72
- let :hi_s, input.fed.single.rates.bracket.hi
73
- let :rate_s, input.fed.single.rates.bracket.rate
74
- let :hi_s_eff, select(hi_s == -1, big_hi, hi_s)
75
- let :amt_s, fn(:clamp, taxable_single - lo_s, 0, hi_s_eff - lo_s)
76
- let :fed_tax_single, fn(:sum, amt_s * rate_s)
77
- let :in_br_s, (taxable_single >= lo_s) & (taxable_single < hi_s_eff)
78
- let :fed_marginal_single, fn(:sum_if, rate_s, in_br_s)
79
- let :fed_eff_single, fed_tax_single / fn(:max, [input.income, 1.0])
80
-
81
- let :ss_tax_s, fn(:min, [input.income, ss_wage_base]) * ss_rate
82
- let :med_tax_s, input.income * med_base_rate
83
- let :addl_med_tax_s, fn(:max, [input.income - addl_threshold_single, 0]) * addl_med_rate
84
- let :fica_tax_single, ss_tax_s + med_tax_s + addl_med_tax_s
85
- let :fica_eff_single, fica_tax_single / fn(:max, [input.income, 1.0])
86
-
87
- let :total_tax_single, fed_tax_single + fica_tax_single + state_tax + local_tax
88
- let :total_eff_single, total_tax_single / fn(:max, [input.income, 1.0])
89
- let :after_tax_single, input.income - total_tax_single
90
- let :take_home_single, after_tax_single - input.retirement_contrib
91
-
92
- # ——— MARRIED JOINT ———
93
- let :taxable_mj, fn(:max, [input.income - input.fed.married_joint.std, 0])
94
- let :lo_mj, input.fed.married_joint.rates.bracket.lo
95
- let :hi_mj, input.fed.married_joint.rates.bracket.hi
96
- let :rate_mj, input.fed.married_joint.rates.bracket.rate
97
- let :hi_mj_eff, select(hi_mj == -1, big_hi, hi_mj)
98
- let :amt_mj, fn(:clamp, taxable_mj - lo_mj, 0, hi_mj_eff - lo_mj)
99
- let :fed_tax_mj, fn(:sum, amt_mj * rate_mj)
100
- let :in_br_mj, (taxable_mj >= lo_mj) & (taxable_mj < hi_mj_eff)
101
- let :fed_marginal_mj, fn(:sum_if, rate_mj, in_br_mj)
102
- let :fed_eff_mj, fed_tax_mj / fn(:max, [input.income, 1.0])
103
-
104
- let :ss_tax_mj, ss_tax_s
105
- let :med_tax_mj, med_tax_s
106
- let :addl_med_tax_mj, fn(:max, [input.income - addl_threshold_mj, 0]) * addl_med_rate
107
- let :fica_tax_mj, ss_tax_mj + med_tax_mj + addl_med_tax_mj
108
- let :fica_eff_mj, fica_tax_mj / fn(:max, [input.income, 1.0])
109
-
110
- let :total_tax_mj, fed_tax_mj + fica_tax_mj + state_tax + local_tax
111
- let :total_eff_mj, total_tax_mj / fn(:max, [input.income, 1.0])
112
- let :after_tax_mj, input.income - total_tax_mj
113
- let :take_home_mj, after_tax_mj - input.retirement_contrib
114
-
115
- # ——— MARRIED SEPARATE ———
116
- let :taxable_ms, fn(:max, [input.income - input.fed.married_separate.std, 0])
117
- let :lo_ms, input.fed.married_separate.rates.bracket.lo
118
- let :hi_ms, input.fed.married_separate.rates.bracket.hi
119
- let :rate_ms, input.fed.married_separate.rates.bracket.rate
120
- let :hi_ms_eff, select(hi_ms == -1, big_hi, hi_ms)
121
- let :amt_ms, fn(:clamp, taxable_ms - lo_ms, 0, hi_ms_eff - lo_ms)
122
- let :fed_tax_ms, fn(:sum, amt_ms * rate_ms)
123
- let :in_br_ms, (taxable_ms >= lo_ms) & (taxable_ms < hi_ms_eff)
124
- let :fed_marginal_ms, fn(:sum_if, rate_ms, in_br_ms)
125
- let :fed_eff_ms, fed_tax_ms / fn(:max, [input.income, 1.0])
126
-
127
- let :ss_tax_ms, ss_tax_s
128
- let :med_tax_ms, med_tax_s
129
- let :addl_med_tax_ms, fn(:max, [input.income - addl_threshold_ms, 0]) * addl_med_rate
130
- let :fica_tax_ms, ss_tax_ms + med_tax_ms + addl_med_tax_ms
131
- let :fica_eff_ms, fica_tax_ms / fn(:max, [input.income, 1.0])
132
-
133
- let :total_tax_ms, fed_tax_ms + fica_tax_ms + state_tax + local_tax
134
- let :total_eff_ms, total_tax_ms / fn(:max, [input.income, 1.0])
135
- let :after_tax_ms, input.income - total_tax_ms
136
- let :take_home_ms, after_tax_ms - input.retirement_contrib
137
-
138
- # ——— HEAD OF HOUSEHOLD ———
139
- let :taxable_hoh, fn(:max, [input.income - input.fed.head_of_household.std, 0])
140
- let :lo_h, input.fed.head_of_household.rates.bracket.lo
141
- let :hi_h, input.fed.head_of_household.rates.bracket.hi
142
- let :rate_h, input.fed.head_of_household.rates.bracket.rate
143
- let :hi_h_eff, select(hi_h == -1, big_hi, hi_h)
144
- let :amt_h, fn(:clamp, taxable_hoh - lo_h, 0, hi_h_eff - lo_h)
145
- let :fed_tax_hoh, fn(:sum, amt_h * rate_h)
146
- let :in_br_h, (taxable_hoh >= lo_h) & (taxable_hoh < hi_h_eff)
147
- let :fed_marginal_hoh, fn(:sum_if, rate_h, in_br_h)
148
- let :fed_eff_hoh, fed_tax_hoh / fn(:max, [input.income, 1.0])
149
-
150
- let :ss_tax_h, ss_tax_s
151
- let :med_tax_h, med_tax_s
152
- let :addl_med_tax_h, fn(:max, [input.income - addl_threshold_hoh, 0]) * addl_med_rate
153
- let :fica_tax_hoh, ss_tax_h + med_tax_h + addl_med_tax_h
154
- let :fica_eff_hoh, fica_tax_hoh / fn(:max, [input.income, 1.0])
155
-
156
- let :total_tax_hoh, fed_tax_hoh + fica_tax_hoh + state_tax + local_tax
157
- let :total_eff_hoh, total_tax_hoh / fn(:max, [input.income, 1.0])
158
- let :after_tax_hoh, input.income - total_tax_hoh
159
- let :take_home_hoh, after_tax_hoh - input.retirement_contrib
160
-
36
+ # per-status federal
37
+ let :taxable, fn(:max, [input.income - input.statuses.status.std, 0])
38
+ let :lo, input.statuses.status.rates.bracket.lo
39
+ let :hi, input.statuses.status.rates.bracket.hi
40
+ let :rate, input.statuses.status.rates.bracket.rate
41
+ let :hi_eff, select(hi == -1, big_hi, hi)
42
+ let :amt, fn(:clamp, taxable - lo, 0, hi_eff - lo)
43
+ let :fed_tax, fn(:sum, amt * rate)
44
+ let :in_br, (taxable >= lo) & (taxable < hi_eff)
45
+ let :fed_marg, fn(:sum_if, rate, in_br)
46
+ let :fed_eff, fed_tax / fn(:max, [input.income, 1.0])
47
+
48
+ # per-status FICA
49
+ let :ss_tax, fn(:min, [input.income, ss_wage_base]) * ss_rate
50
+ let :med_tax, input.income * med_base_rate
51
+ let :addl_med_tax, fn(:max, [input.income - input.statuses.status.addl_threshold, 0]) * addl_med_rate
52
+ let :fica_tax, ss_tax + med_tax + addl_med_tax
53
+ let :fica_eff, fica_tax / fn(:max, [input.income, 1.0])
54
+
55
+ # totals per status
56
+ let :total_tax, fed_tax + fica_tax + state_tax + local_tax
57
+ let :total_eff, total_tax / fn(:max, [input.income, 1.0])
58
+ let :after_tax, input.income - total_tax
59
+ let :take_home, after_tax - input.retirement_contrib
60
+
61
+ # array of result objects, one per status
161
62
  value :summary, {
162
- single: {
163
- federal: { marginal: fed_marginal_single, effective: fed_eff_single, tax: fed_tax_single },
164
- fica: { effective: fica_eff_single, tax: fica_tax_single },
165
- state: { marginal: input.state_rate, effective: input.state_rate, tax: state_tax },
166
- local: { marginal: input.local_rate, effective: input.local_rate, tax: local_tax },
167
- total: { effective: total_eff_single, tax: total_tax_single },
168
- after_tax: after_tax_single,
169
- retirement_contrib: input.retirement_contrib,
170
- take_home: take_home_single
171
- },
172
- married_joint: {
173
- federal: { marginal: fed_marginal_mj, effective: fed_eff_mj, tax: fed_tax_mj },
174
- fica: { effective: fica_eff_mj, tax: fica_tax_mj },
175
- state: { marginal: input.state_rate, effective: input.state_rate, tax: state_tax },
176
- local: { marginal: input.local_rate, effective: input.local_rate, tax: local_tax },
177
- total: { effective: total_eff_mj, tax: total_tax_mj },
178
- after_tax: after_tax_mj,
179
- retirement_contrib: input.retirement_contrib,
180
- take_home: take_home_mj
181
- },
182
- married_separate: {
183
- federal: { marginal: fed_marginal_ms, effective: fed_eff_ms, tax: fed_tax_ms },
184
- fica: { effective: fica_eff_ms, tax: fica_tax_ms },
185
- state: { marginal: input.state_rate, effective: input.state_rate, tax: state_tax },
186
- local: { marginal: input.local_rate, effective: input.local_rate, tax: local_tax },
187
- total: { effective: total_eff_ms, tax: total_tax_ms },
188
- after_tax: after_tax_ms,
189
- retirement_contrib: input.retirement_contrib,
190
- take_home: take_home_ms
191
- },
192
- head_of_household: {
193
- federal: { marginal: fed_marginal_hoh, effective: fed_eff_hoh, tax: fed_tax_hoh },
194
- fica: { effective: fica_eff_hoh, tax: fica_tax_hoh },
195
- state: { marginal: input.state_rate, effective: input.state_rate, tax: state_tax },
196
- local: { marginal: input.local_rate, effective: input.local_rate, tax: local_tax },
197
- total: { effective: total_eff_hoh, tax: total_tax_hoh },
198
- after_tax: after_tax_hoh,
199
- retirement_contrib: input.retirement_contrib,
200
- take_home: take_home_hoh
201
- }
63
+ filing_status: input.statuses.status.name,
64
+ federal: { marginal: fed_marg, effective: fed_eff, tax: fed_tax },
65
+ fica: { effective: fica_eff, tax: fica_tax },
66
+ state: { marginal: input.state_rate, effective: input.state_rate, tax: state_tax },
67
+ local: { marginal: input.local_rate, effective: input.local_rate, tax: local_tax },
68
+ total: { effective: total_eff, tax: total_tax },
69
+ after_tax: after_tax,
70
+ retirement_contrib: input.retirement_contrib,
71
+ take_home: take_home
202
72
  }
203
73
  end
@@ -3,7 +3,7 @@
3
3
  %t1 = const 100 :: integer
4
4
  %t2 = const 85 :: integer
5
5
  %t3 = const 92 :: integer
6
- %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer>
6
+ %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer, integer, integer>
7
7
  yield %t4
8
8
  )
9
9
  (Declaration first_score
@@ -3,7 +3,7 @@
3
3
  %t1 = const 100 :: integer
4
4
  %t2 = const 85 :: integer
5
5
  %t3 = const 92 :: integer
6
- %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer>
6
+ %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer, integer, integer>
7
7
  yield %t4
8
8
  )
9
9
  (Declaration first_score
@@ -3,7 +3,7 @@
3
3
  %t1 = const 100 :: integer
4
4
  %t2 = const 85 :: integer
5
5
  %t3 = const 92 :: integer
6
- %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer>
6
+ %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer, integer, integer>
7
7
  yield %t4
8
8
  )
9
9
  (Declaration first_score
@@ -3,7 +3,7 @@
3
3
  %t1 = const 100 :: integer
4
4
  %t2 = const 85 :: integer
5
5
  %t3 = const 92 :: integer
6
- %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer>
6
+ %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer, integer, integer>
7
7
  yield %t4
8
8
  )
9
9
  (Declaration first_score
@@ -3,7 +3,7 @@
3
3
  %t1 = const 100 :: integer
4
4
  %t2 = const 85 :: integer
5
5
  %t3 = const 92 :: integer
6
- %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer>
6
+ %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer, integer, integer>
7
7
  yield %t4
8
8
  )
9
9
  (Declaration first_score
@@ -3,7 +3,7 @@
3
3
  %t1 = const 100 :: integer
4
4
  %t2 = const 85 :: integer
5
5
  %t3 = const 92 :: integer
6
- %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer>
6
+ %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer, integer, integer>
7
7
  yield %t4
8
8
  )
9
9
  (Declaration first_score
@@ -3,7 +3,7 @@
3
3
  %t1 = const 100 :: integer
4
4
  %t2 = const 85 :: integer
5
5
  %t3 = const 92 :: integer
6
- %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer>
6
+ %t4 = make_tuple(%t1, %t2, %t3) :: tuple<integer, integer, integer>
7
7
  yield %t4
8
8
  )
9
9
  (Declaration first_score
@@ -7,13 +7,13 @@
7
7
  )
8
8
  )
9
9
  (VALUE first_score
10
- (Call :"core.at"
10
+ (Call :at
11
11
  (Ref scores)
12
12
  (Const 0)
13
13
  )
14
14
  )
15
15
  (VALUE second_score
16
- (Call :"core.at"
16
+ (Call :at
17
17
  (Ref scores)
18
18
  (Const 1)
19
19
  )
@@ -1,5 +1,5 @@
1
1
  # Autogenerated by Kumi Codegen
2
- module Kumi::Compiled::KUMI_ee04981edda49a2945319fd2a0457a60507a9e934cb7a4fdf3eeb6545dd1ef47
2
+ module Kumi::Compiled::KUMI_57ccd5ab1fc1187ce0abd20aa81d60fe4c33ce2438cccd991cd4fbd32e5a67e8
3
3
  def self.from(input_data = nil)
4
4
  instance = Object.new
5
5
  instance.extend(self)
@@ -4,8 +4,8 @@
4
4
  (Const 100) :: [] -> integer
5
5
  (Const 85) :: [] -> integer
6
6
  (Const 92) :: [] -> integer
7
- ) :: [] -> tuple<integer>
8
- ) :: [] -> tuple<integer>
7
+ ) :: [] -> tuple<integer, integer, integer>
8
+ ) :: [] -> tuple<integer, integer, integer>
9
9
  (VALUE first_score
10
10
  (Const 100) :: [] -> integer
11
11
  ) :: [] -> integer
data/lib/kumi/analyzer.rb CHANGED
@@ -13,18 +13,17 @@ module Kumi
13
13
  Passes::DeclarationValidator, # 4. Checks the basic structure of each rule.
14
14
  Passes::SemanticConstraintValidator, # 5. Validates DSL semantic constraints at AST level.
15
15
  Passes::DependencyResolver, # 6. Builds the dependency graph with conditional dependencies.
16
- Passes::UnsatDetector, # 7. Detects unsatisfiable constraints and analyzes cascade mutual exclusion.
17
- Passes::Toposorter, # 8. Creates the final evaluation order, allowing safe cycles.
18
- # Passes::BroadcastDetector, # 9. Detects which operations should be broadcast over arrays.
19
- # Passes::TypeInferencerPass, # 10. Infers types for all declarations (uses vectorization metadata).
20
- # Passes::TypeChecker, # 11. Validates types using inferred information.
21
- Passes::InputAccessPlannerPass # 12. Plans access strategies for input fields.
22
- # Passes::ScopeResolutionPass, # 13. Plans execution scope and lifting needs for declarations.
23
- # Passes::JoinReducePlanningPass, # 14. Plans join/reduce operations (Generates IR Structs)
24
- # Passes::LowerToIRPass, # 15. Lowers the schema to IR (Generates IR Structs)
25
- # Passes::LoadInputCSE, # 16. Eliminates redundant load_input operations
26
- # Passes::IRDependencyPass, # 17. Extracts IR-level dependencies for VM execution optimization
27
- # Passes::IRExecutionSchedulePass # 18. Builds a precomputed execution schedule.
16
+ Passes::Toposorter, # 7. Creates the final evaluation order, allowing safe cycles.
17
+ # Passes::BroadcastDetector, # 8. Detects which operations should be broadcast over arrays.
18
+ # Passes::TypeInferencerPass, # 9. Infers types for all declarations (uses vectorization metadata).
19
+ # Passes::TypeChecker, # 10. Validates types using inferred information.
20
+ Passes::InputAccessPlannerPass # 11. Plans access strategies for input fields.
21
+ # Passes::ScopeResolutionPass, # 12. Plans execution scope and lifting needs for declarations.
22
+ # Passes::JoinReducePlanningPass, # 13. Plans join/reduce operations (Generates IR Structs)
23
+ # Passes::LowerToIRPass, # 14. Lowers the schema to IR (Generates IR Structs)
24
+ # Passes::LoadInputCSE, # 15. Eliminates redundant load_input operations
25
+ # Passes::IRDependencyPass, # 16. Extracts IR-level dependencies for VM execution optimization
26
+ # Passes::IRExecutionSchedulePass # 17. Builds a precomputed execution schedule.
28
27
  ].freeze
29
28
 
30
29
  # Pipeline passes for the determinisitic NAST->LIR approach
@@ -33,6 +32,7 @@ module Kumi
33
32
  Passes::ConstantFoldingPass, # Folds constant expressions in NAST
34
33
  Passes::NASTDimensionalAnalyzerPass, # Extracts dimensional and type metadata from NAST
35
34
  Passes::SNASTPass, # Creates Semantic NAST with dimensional stamps and execution plans
35
+ Passes::UnsatDetector, # Detects impossible constraints with resolved function IDs and SNAST metadata
36
36
  Passes::OutputSchemaPass, # Builds minimal output schema from SNAST
37
37
  Passes::AttachTerminalInfoPass, # Attaches key_chain info to InputRef nodes
38
38
  Passes::AttachAnchorsPass,