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