1 //: Comparison primitives
  2 
  3 :(before "End Primitive Recipe Declarations")
  4 EQUAL,
  5 :(before "End Primitive Recipe Numbers")
  6 put(Recipe_ordinal, "equal", EQUAL);
  7 :(before "End Primitive Recipe Checks")
  8 case EQUAL: {
  9   if (SIZE(inst.ingredients) <= 1) {
 10     raise << maybe(get(Recipe, r).name) << "'equal' needs at least two ingredients to compare in '" << to_original_string(inst) << "'\n" << end();
 11     break;
 12   }
 13   const reagent& exemplar = inst.ingredients.at(0);
 14   for (int i = /*skip exemplar*/1;  i < SIZE(inst.ingredients);  ++i) {
 15     if (!types_match(inst.ingredients.at(i), exemplar) && !types_match(exemplar, inst.ingredients.at(i))) {
 16       raise << maybe(get(Recipe, r).name) << "'equal' expects ingredients to be all of the same type, but got '" << to_original_string(inst) << "'\n" << end();
 17       goto finish_checking_instruction;
 18     }
 19   }
 20   if (SIZE(inst.products) > 1) {
 21     raise << maybe(get(Recipe, r).name) << "'equal' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
 22     break;
 23   }
 24   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_boolean(inst.products.at(0))) {
 25     raise << maybe(get(Recipe, r).name) << "'equal' should yield a boolean, but got '" << inst.products.at(0).original_string << "'\n" << end();
 26     break;
 27   }
 28   break;
 29 }
 30 :(before "End Primitive Recipe Implementations")
 31 case EQUAL: {
 32   vector<double>& exemplar = ingredients.at(0);
 33   bool result = true;
 34   for (int i = /*skip exemplar*/1;  i < SIZE(ingredients);  ++i) {
 35     if (SIZE(ingredients.at(i)) != SIZE(exemplar)) {
 36       result = false;
 37       break;
 38     }
 39     if (!equal(ingredients.at(i).begin(), ingredients.at(i).end(), exemplar.begin())) {
 40       result = false;
 41       break;
 42     }
 43   }
 44   products.resize(1);
 45   products.at(0).push_back(result);
 46   break;
 47 }
 48 
 49 :(scenario equal)
 50 def main [
 51   1:num <- copy 34
 52   2:num <- copy 33
 53   3:bool <- equal 1:num, 2:num
 54 ]
 55 +mem: location 1 is 34
 56 +mem: location 2 is 33
 57 +mem: storing 0 in location 3
 58 
 59 :(scenario equal_2)
 60 def main [
 61   1:num <- copy 34
 62   2:num <- copy 34
 63   3:bool <- equal 1:num, 2:num
 64 ]
 65 +mem: location 1 is 34
 66 +mem: location 2 is 34
 67 +mem: storing 1 in location 3
 68 
 69 :(scenario equal_multiple)
 70 def main [
 71   1:bool <- equal 34, 34, 34
 72 ]
 73 +mem: storing 1 in location 1
 74 
 75 :(scenario equal_multiple_2)
 76 def main [
 77   1:bool <- equal 34, 34, 35
 78 ]
 79 +mem: storing 0 in location 1
 80 
 81 :(before "End Primitive Recipe Declarations")
 82 NOT_EQUAL,
 83 :(before "End Primitive Recipe Numbers")
 84 put(Recipe_ordinal, "not-equal", NOT_EQUAL);
 85 :(before "End Primitive Recipe Checks")
 86 case NOT_EQUAL: {
 87   if (SIZE(inst.ingredients) != 2) {
 88     raise << maybe(get(Recipe, r).name) << "'equal' needs two ingredients to compare in '" << to_original_string(inst) << "'\n" << end();
 89     break;
 90   }
 91   const reagent& exemplar = inst.ingredients.at(0);
 92   if (!types_match(inst.ingredients.at(1), exemplar) && !types_match(exemplar, inst.ingredients.at(1))) {
 93     raise << maybe(get(Recipe, r).name) << "'equal' expects ingredients to be all of the same type, but got '" << to_original_string(inst) << "'\n" << end();
 94     goto finish_checking_instruction;
 95   }
 96   if (SIZE(inst.products) > 1) {
 97     raise << maybe(get(Recipe, r).name) << "'equal' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
 98     break;
 99   }
100   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_boolean(inst.products.at(0))) {
101     raise << maybe(get(Recipe, r).name) << "'equal' should yield a boolean, but got '" << inst.products.at(0).original_string << "'\n" << end();
102     break;
103   }
104   break;
105 }
106 :(before "End Primitive Recipe Implementations")
107 case NOT_EQUAL: {
108   vector<double>& exemplar = ingredients.at(0);
109   products.resize(1);
110   if (SIZE(ingredients.at(1)) != SIZE(exemplar)) {
111     products.at(0).push_back(true);
112     break;
113   }
114   bool equal_ingredients = equal(ingredients.at(1).begin(), ingredients.at(1).end(), exemplar.begin());
115   products.at(0).push_back(!equal_ingredients);
116   break;
117 }
118 
119 :(scenario not_equal)
120 def main [
121   1:num <- copy 34
122   2:num <- copy 33
123   3:bool <- not-equal 1:num, 2:num
124 ]
125 +mem: location 1 is 34
126 +mem: location 2 is 33
127 +mem: storing 1 in location 3
128 
129 :(scenario not_equal_2)
130 def main [
131   1:num <- copy 34
132   2:num <- copy 34
133   3:bool <- not-equal 1:num, 2:num
134 ]
135 +mem: location 1 is 34
136 +mem: location 2 is 34
137 +mem: storing 0 in location 3
138 
139 :(before "End Primitive Recipe Declarations")
140 GREATER_THAN,
141 :(before "End Primitive Recipe Numbers")
142 put(Recipe_ordinal, "greater-than", GREATER_THAN);
143 :(before "End Primitive Recipe Checks")
144 case GREATER_THAN: {
145   if (SIZE(inst.ingredients) <= 1) {
146     raise << maybe(get(Recipe, r).name) << "'greater-than' needs at least two ingredients to compare in '" << to_original_string(inst) << "'\n" << end();
147     break;
148   }
149   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
150     if (!is_mu_number(inst.ingredients.at(i))) {
151       raise << maybe(get(Recipe, r).name) << "'greater-than' can only compare numbers; got '" << inst.ingredients.at(i).original_string << "'\n" << end();
152       goto finish_checking_instruction;
153     }
154   }
155   if (SIZE(inst.products) > 1) {
156     raise << maybe(get(Recipe, r).name) << "'greater-than' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
157     break;
158   }
159   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_boolean(inst.products.at(0))) {
160     raise << maybe(get(Recipe, r).name) << "'greater-than' should yield a boolean, but got '" << inst.products.at(0).original_string << "'\n" << end();
161     break;
162   }
163   break;
164 }
165 :(before "End Primitive Recipe Implementations")
166 case GREATER_THAN: {
167   bool result = true;
168   for (int i = /**/1;  i < SIZE(ingredients);  ++i) {
169     if (ingredients.at(i-1).at(0) <= ingredients.at(i).at(0)) {
170       result = false;
171     }
172   }
173   products.resize(1);
174   products.at(0).push_back(result);
175   break;
176 }
177 
178 :(scenario greater_than)
179 def main [
180   1:num <- copy 34
181   2:num <- copy 33
182   3:bool <- greater-than 1:num, 2:num
183 ]
184 +mem: storing 1 in location 3
185 
186 :(scenario greater_than_2)
187 def main [
188   1:num <- copy 34
189   2:num <- copy 34
190   3:bool <- greater-than 1:num, 2:num
191 ]
192 +mem: storing 0 in location 3
193 
194 :(scenario greater_than_multiple)
195 def main [
196   1:bool <- greater-than 36, 35, 34
197 ]
198 +mem: storing 1 in location 1
199 
200 :(scenario greater_than_multiple_2)
201 def main [
202   1:bool <- greater-than 36, 35, 35
203 ]
204 +mem: storing 0 in location 1
205 
206 :(before "End Primitive Recipe Declarations")
207 LESSER_THAN,
208 :(before "End Primitive Recipe Numbers")
209 put(Recipe_ordinal, "lesser-than", LESSER_THAN);
210 :(before "End Primitive Recipe Checks")
211 case LESSER_THAN: {
212   if (SIZE(inst.ingredients) <= 1) {
213     raise << maybe(get(Recipe, r).name) << "'lesser-than' needs at least two ingredients to compare in '" << to_original_string(inst) << "'\n" << end();
214     break;
215   }
216   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
217     if (!is_mu_number(inst.ingredients.at(i))) {
218       raise << maybe(get(Recipe, r).name) << "'lesser-than' can only compare numbers; got '" << inst.ingredients.at(i).original_string << "'\n" << end();
219       goto finish_checking_instruction;
220     }
221   }
222   if (SIZE(inst.products) > 1) {
223     raise << maybe(get(Recipe, r).name) << "'lesser-than' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
224     break;
225   }
226   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_boolean(inst.products.at(0))) {
227     raise << maybe(get(Recipe, r).name) << "'lesser-than' should yield a boolean, but got '" << inst.products.at(0).original_string << "'\n" << end();
228     break;
229   }
230   break;
231 }
232 :(before "End Primitive Recipe Implementations")
233 case LESSER_THAN: {
234   bool result = true;
235   for (int i = /**/1;  i < SIZE(ingredients);  ++i) {
236     if (ingredients.at(i-1).at(0) >= ingredients.at(i).at(0)) {
237       result = false;
238     }
239   }
240   products.resize(1);
241   products.at(0).push_back(result);
242   break;
243 }
244 
245 :(scenario lesser_than)
246 def main [
247   1:num <- copy 32
248   2:num <- copy 33
249   3:bool <- lesser-than 1:num, 2:num
250 ]
251 +mem: storing 1 in location 3
252 
253 :(scenario lesser_than_2)
254 def main [
255   1:num <- copy 34
256   2:num <- copy 33
257   3:bool <- lesser-than 1:num, 2:num
258 ]
259 +mem: storing 0 in location 3
260 
261 :(scenario lesser_than_multiple)
262 def main [
263   1:bool <- lesser-than 34, 35, 36
264 ]
265 +mem: storing 1 in location 1
266 
267 :(scenario lesser_than_multiple_2)
268 def main [
269   1:bool <- lesser-than 34, 35, 35
270 ]
271 +mem: storing 0 in location 1
272 
273 :(before "End Primitive Recipe Declarations")
274 GREATER_OR_EQUAL,
275 :(before "End Primitive Recipe Numbers")
276 put(Recipe_ordinal, "greater-or-equal", GREATER_OR_EQUAL);
277 :(before "End Primitive Recipe Checks")
278 case GREATER_OR_EQUAL: {
279   if (SIZE(inst.ingredients) <= 1) {
280     raise << maybe(get(Recipe, r).name) << "'greater-or-equal' needs at least two ingredients to compare in '" << to_original_string(inst) << "'\n" << end();
281     break;
282   }
283   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
284     if (!is_mu_number(inst.ingredients.at(i))) {
285       raise << maybe(get(Recipe, r).name) << "'greater-or-equal' can only compare numbers; got '" << inst.ingredients.at(i).original_string << "'\n" << end();
286       goto finish_checking_instruction;
287     }
288   }
289   if (SIZE(inst.products) > 1) {
290     raise << maybe(get(Recipe, r).name) << "'greater-or-equal' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
291     break;
292   }
293   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_boolean(inst.products.at(0))) {
294     raise << maybe(get(Recipe, r).name) << "'greater-or-equal' should yield a boolean, but got '" << inst.products.at(0).original_string << "'\n" << end();
295     break;
296   }
297   break;
298 }
299 :(before "End Primitive Recipe Implementations")
300 case GREATER_OR_EQUAL: {
301   bool result = true;
302   for (int i = /**/1;  i < SIZE(ingredients);  ++i) {
303     if (ingredients.at(i-1).at(0) < ingredients.at(i).at(0)) {
304       result = false;
305     }
306   }
307   products.resize(1);
308   products.at(0).push_back(result);
309   break;
310 }
311 
312 :(scenario greater_or_equal)
313 def main [
314   1:num <- copy 34
315   2:num <- copy 33
316   3:bool <- greater-or-equal 1:num, 2:num
317 ]
318 +mem: storing 1 in location 3
319 
320 :(scenario greater_or_equal_2)
321 def main [
322   1:num <- copy 34
323   2:num <- copy 34
324   3:bool <- greater-or-equal 1:num, 2:num
325 ]
326 +mem: storing 1 in location 3
327 
328 :(scenario greater_or_equal_3)
329 def main [
330   1:num <- copy 34
331   2:num <- copy 35
332   3:bool <- greater-or-equal 1:num, 2:num
333 ]
334 +mem: storing 0 in location 3
335 
336 :(scenario greater_or_equal_multiple)
337 def main [
338   1:bool <- greater-or-equal 36, 35, 35
339 ]
340 +mem: storing 1 in location 1
341 
342 :(scenario greater_or_equal_multiple_2)
343 def main [
344   1:bool <- greater-or-equal 36, 35, 36
345 ]
346 +mem: storing 0 in location 1
347 
348 :(before "End Primitive Recipe Declarations")
349 LESSER_OR_EQUAL,
350 :(before "End Primitive Recipe Numbers")
351 put(Recipe_ordinal, "lesser-or-equal", LESSER_OR_EQUAL);
352 :(before "End Primitive Recipe Checks")
353 case LESSER_OR_EQUAL: {
354   if (SIZE(inst.ingredients) <= 1) {
355     raise << maybe(get(Recipe, r).name) << "'lesser-or-equal' needs at least two ingredients to compare in '" << to_original_string(inst) << "'\n" << end();
356     break;
357   }
358   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
359     if (!is_mu_number(inst.ingredients.at(i))) {
360       raise << maybe(get(Recipe, r).name) << "'lesser-or-equal' can only compare numbers; got '" << inst.ingredients.at(i).original_string << "'\n" << end();
361       goto finish_checking_instruction;
362     }
363   }
364   if (SIZE(inst.products) > 1) {
365     raise << maybe(get(Recipe, r).name) << "'greater-or-equal' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
366     break;
367   }
368   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_boolean(inst.products.at(0))) {
369     raise << maybe(get(Recipe, r).name) << "'greater-or-equal' should yield a boolean, but got '" << inst.products.at(0).original_string << "'\n" << end();
370     break;
371   }
372   break;
373 }
374 :(before "End Primitive Recipe Implementations")
375 case LESSER_OR_EQUAL: {
376   bool result = true;
377   for (int i = /**/1;  i < SIZE(ingredients);  ++i) {
378     if (ingredients.at(i-1).at(0) > ingredients.at(i).at(0)) {
379       result = false;
380     }
381   }
382   products.resize(1);
383   products.at(0).push_back(result);
384   break;
385 }
386 
387 :(scenario lesser_or_equal)
388 def main [
389   1:num <- copy 32
390   2:num <- copy 33
391   3:bool <- lesser-or-equal 1:num, 2:num
392 ]
393 +mem: storing 1 in location 3
394 
395 :(scenario lesser_or_equal_2)
396 def main [
397   1:num <- copy 33
398   2:num <- copy 33
399   3:bool <- lesser-or-equal 1:num, 2:num
400 ]
401 +mem: storing 1 in location 3
402 
403 :(scenario lesser_or_equal_3)
404 def main [
405   1:num <- copy 34
406   2:num <- copy 33
407   3:bool <- lesser-or-equal 1:num, 2:num
408 ]
409 +mem: storing 0 in location 3
410 
411 :(scenario lesser_or_equal_multiple)
412 def main [
413   1:bool <- lesser-or-equal 34, 35, 35
414 ]
415 +mem: storing 1 in location 1
416 
417 :(scenario lesser_or_equal_multiple_2)
418 def main [
419   1:bool <- lesser-or-equal 34, 35, 34
420 ]
421 +mem: storing 0 in location 1
422 
423 :(before "End Primitive Recipe Declarations")
424 MAX,
425 :(before "End Primitive Recipe Numbers")
426 put(Recipe_ordinal, "max", MAX);
427 :(before "End Primitive Recipe Checks")
428 case MAX: {
429   if (SIZE(inst.ingredients) <= 1) {
430     raise << maybe(get(Recipe, r).name) << "'max' needs at least two ingredients to compare in '" << to_original_string(inst) << "'\n" << end();
431     break;
432   }
433   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
434     if (!is_mu_number(inst.ingredients.at(i))) {
435       raise << maybe(get(Recipe, r).name) << "'max' can only compare numbers; got '" << inst.ingredients.at(i).original_string << "'\n" << end();
436       goto finish_checking_instruction;
437     }
438   }
439   if (SIZE(inst.products) > 1) {
440     raise << maybe(get(Recipe, r).name) << "'max' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
441     break;
442   }
443   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
444     raise << maybe(get(Recipe, r).name) << "'max' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
445     break;
446   }
447   break;
448 }
449 :(before "End Primitive Recipe Implementations")
450 case MAX: {
451   int result = ingredients.at(0).at(0);
452   for (int i = /**/1;  i < SIZE(ingredients);  ++i) {
453     if (ingredients.at(i).at(0) > result) {
454       result = ingredients.at(i).at(0);
455     }
456   }
457   products.resize(1);
458   products.at(0).push_back(result);
459   break;
460 }
461 
462 :(before "End Primitive Recipe Declarations")
463 MIN,
464 :(before "End Primitive Recipe Numbers")
465 put(Recipe_ordinal, "min", MIN);
466 :(before "End Primitive Recipe Checks")
467 case MIN: {
468   if (SIZE(inst.ingredients) <= 1) {
469     raise << maybe(get(Recipe, r).name) << "'min' needs at least two ingredients to compare in '" << to_original_string(inst) << "'\n" << end();
470     break;
471   }
472   for (int i = 0;  i < SIZE(inst.ingredients);  ++i) {
473     if (!is_mu_number(inst.ingredients.at(i))) {
474       raise << maybe(get(Recipe, r).name) << "'min' can only compare numbers; got '" << inst.ingredients.at(i).original_string << "'\n" << end();
475       goto finish_checking_instruction;
476     }
477   }
478   if (SIZE(inst.products) > 1) {
479     raise << maybe(get(Recipe, r).name) << "'min' yields exactly one product in '" << to_original_string(inst) << "'\n" << end();
480     break;
481   }
482   if (!inst.products.empty() && !is_dummy(inst.products.at(0)) && !is_mu_number(inst.products.at(0))) {
483     raise << maybe(get(Recipe, r).name) << "'min' should yield a number, but got '" << inst.products.at(0).original_string << "'\n" << end();
484     break;
485   }
486   break;
487 }
488 :(before "End Primitive Recipe Implementations")
489 case MIN: {
490   int result = ingredients.at(0).at(0);
491   for (int i = /**/1;  i < SIZE(ingredients);  ++i) {
492     if (ingredients.at(i).at(0) < result) {
493       result = ingredients.at(i).at(0);
494     }
495   }
496   products.resize(1);
497   products.at(0).push_back(result);
498   break;
499 }