1
2 :(before "End Globals")
3
4 map<recipe_ordinal, recipe> Recipe;
5
6 map<string, recipe_ordinal> Recipe_ordinal;
7 recipe_ordinal Next_recipe_ordinal = 1;
8
9
10
11
12
13 :(after "Types")
14 typedef int recipe_ordinal;
15
16 :(before "End Types")
17
18
19 struct recipe {
20 string name;
21 vector<instruction> steps;
22
23 recipe();
24 };
25
26 :(before "struct recipe")
27
28
29
30
31
32 struct instruction {
33 bool is_label;
34 string label;
35 string name;
36 string original_string;
37 recipe_ordinal operation;
38 vector<reagent> ingredients;
39 vector<reagent> products;
40
41 instruction();
42 void clear();
43 bool is_empty();
44 };
45
46 :(before "struct instruction")
47
48
49
50
51 struct reagent {
52 string original_string;
53 string name;
54 type_tree* type;
55 vector<pair<string, string_tree*> > properties;
56 double value;
57 bool initialized;
58
59 reagent(const string& s);
60 reagent() :type(NULL), value(0), initialized(false) {}
61 reagent(type_tree* t) :type(t), value(0), initialized(false) {}
62 ~reagent();
63 void clear();
64 reagent(const reagent& original);
65 reagent& operator=(const reagent& original);
66 void set_value(double v) { value = v; initialized = true; }
67 };
68
69 :(before "struct reagent")
70
71
72 struct type_tree {
73 bool atom;
74 string name;
75 type_ordinal value;
76 type_tree* left;
77 type_tree* right;
78 ~type_tree();
79 type_tree(const type_tree& original);
80
81 explicit type_tree(string name);
82 type_tree(string name, type_ordinal v) :atom(true), name(name), value(v), left(NULL), right(NULL) {}
83
84 type_tree(type_tree* l, type_tree* r) :atom(false), value(0), left(l), right(r) {}
85 type_tree& operator=(const type_tree& original);
86 bool operator==(const type_tree& other) const;
87 bool operator!=(const type_tree& other) const { return !operator==(other); }
88 bool operator<(const type_tree& other) const;
89 bool operator>(const type_tree& other) const { return other.operator<(*this); }
90 };
91
92 struct string_tree {
93 bool atom;
94 string value;
95 string_tree* left;
96 string_tree* right;
97 ~string_tree();
98 string_tree(const string_tree& original);
99
100 explicit string_tree(string v) :atom(true), value(v), left(NULL), right(NULL) {}
101
102 string_tree(string_tree* l, string_tree* r) :atom(false), left(l), right(r) {}
103 };
104
105
106 :(code)
107 type_tree::type_tree(string name) :atom(true), name(name), value(get(Type_ordinal, name)), left(NULL), right(NULL) {}
108
109 :(before "End Globals")
110
111 map<int, double> Memory;
112 :(before "End Reset")
113 Memory.clear();
114
115 :(after "Types")
116
117
118
119
120
121
122
123
124 typedef int type_ordinal;
125 :(before "End Globals")
126 map<string, type_ordinal> Type_ordinal;
127 map<type_ordinal, type_info> Type;
128 type_ordinal Next_type_ordinal = 1;
129 :(code)
130 void setup_types() {
131 Type.clear(); Type_ordinal.clear();
132 put(Type_ordinal, "literal", 0);
133 Next_type_ordinal = 1;
134
135 type_ordinal number = put(Type_ordinal, "number", Next_type_ordinal++);
136 get_or_insert(Type, number).name = "number";
137 put(Type_ordinal, "location", number);
138 type_ordinal address = put(Type_ordinal, "address", Next_type_ordinal++);
139 get_or_insert(Type, address).name = "address";
140 type_ordinal boolean = put(Type_ordinal, "boolean", Next_type_ordinal++);
141 get_or_insert(Type, boolean).name = "boolean";
142 type_ordinal character = put(Type_ordinal, "character", Next_type_ordinal++);
143 get_or_insert(Type, character).name = "character";
144
145
146 type_ordinal array = put(Type_ordinal, "array", Next_type_ordinal++);
147 get_or_insert(Type, array).name = "array";
148
149 }
150 void teardown_types() {
151 for (map<type_ordinal, type_info>::iterator p = Type.begin(); p != Type.end(); ++p) {
152 for (int i = 0; i < SIZE(p->second.elements); ++i)
153 p->second.elements.clear();
154 }
155 Type_ordinal.clear();
156 }
157 :(before "End One-time Setup")
158 setup_types();
159 atexit(teardown_types);
160
161 :(before "End Types")
162
163
164
165
166
167
168
169
170
171
172 enum kind_of_type {
173 PRIMITIVE,
174 CONTAINER,
175 EXCLUSIVE_CONTAINER
176 };
177
178 struct type_info {
179 string name;
180 kind_of_type kind;
181 vector<reagent> elements;
182
183 type_info() :kind(PRIMITIVE) {
184
185 }
186 };
187
188 enum primitive_recipes {
189 IDLE = 0,
190 COPY,
191
192 MAX_PRIMITIVE_RECIPES,
193 };
194 :(code)
195
196
197
198
199 void setup_recipes() {
200 Recipe.clear(); Recipe_ordinal.clear();
201 put(Recipe_ordinal, "idle", IDLE);
202
203 put(Recipe_ordinal, "copy", COPY);
204
205 }
206
207
208
209
210 :(before "End One-time Setup")
211 setup_recipes();
212 assert(MAX_PRIMITIVE_RECIPES < 200);
213 Next_recipe_ordinal = 200;
214 put(Recipe_ordinal, "main", Next_recipe_ordinal++);
215
216 :(before "End Commandline Parsing")
217 assert(Next_recipe_ordinal < 1000);
218 :(before "End Reset")
219 Next_recipe_ordinal = 1000;
220
221
222
223
224 :(before "End Globals")
225 map<string, recipe_ordinal> Recipe_ordinal_snapshot;
226 map<recipe_ordinal, recipe> Recipe_snapshot;
227 map<string, type_ordinal> Type_ordinal_snapshot;
228 map<type_ordinal, type_info> Type_snapshot;
229 :(before "End One-time Setup")
230 save_snapshots();
231 :(before "End Reset")
232 restore_snapshots();
233
234 :(code)
235 void save_snapshots() {
236 Recipe_ordinal_snapshot = Recipe_ordinal;
237 Recipe_snapshot = Recipe;
238 Type_ordinal_snapshot = Type_ordinal;
239 Type_snapshot = Type;
240
241 }
242
243 void restore_snapshots() {
244 Recipe = Recipe_snapshot;
245 Recipe_ordinal = Recipe_ordinal_snapshot;
246 restore_non_recipe_snapshots();
247 }
248
249 void restore_non_recipe_snapshots() {
250 Type_ordinal = Type_ordinal_snapshot;
251 Type = Type_snapshot;
252
253 }
254
255
256
257 :(code)
258 recipe::recipe() {
259
260 }
261
262 instruction::instruction() :is_label(false), operation(IDLE) {
263
264 }
265 void instruction::clear() {
266 is_label=false;
267 label.clear();
268 name.clear();
269 operation=IDLE;
270 ingredients.clear();
271 products.clear();
272 original_string.clear();
273
274 }
275 bool instruction::is_empty() { return !is_label && name.empty(); }
276
277
278 reagent::reagent(const string& s) :original_string(s), type(NULL), value(0), initialized(false) {
279
280 istringstream in(s);
281 in >> std::noskipws;
282
283 istringstream first_row(slurp_until(in, '/'));
284 first_row >> std::noskipws;
285 name = slurp_until(first_row, ':');
286 string_tree* type_names = parse_property_list(first_row);
287
288 type = new_type_tree(type_names);
289 delete type_names;
290
291 if (is_integer(name) && type == NULL)
292 type = new type_tree("literal");
293 if (name == "_" && type == NULL)
294 type = new type_tree("literal");
295
296 slurp_properties(in, properties);
297
298 }
299
300 void slurp_properties(istream& in, vector<pair<string, string_tree*> >& out) {
301 while (has_data(in)) {
302 istringstream row(slurp_until(in, '/'));
303 row >> std::noskipws;
304 string key = slurp_until(row, ':');
305 string_tree* value = parse_property_list(row);
306 out.push_back(pair<string, string_tree*>(key, value));
307 }
308 }
309
310 string_tree* parse_property_list(istream& in) {
311 skip_whitespace_but_not_newline(in);
312 if (!has_data(in)) return NULL;
313 string_tree* first = new string_tree(slurp_until(in, ':'));
314 if (!has_data(in)) return first;
315 string_tree* rest = parse_property_list(in);
316 if (!has_data(in) && rest->atom)
317 return new string_tree(first, new string_tree(rest, NULL));
318 return new string_tree(first, rest);
319 }
320 :(before "End Unit Tests")
321 void test_parse_property_list_atom() {
322 istringstream in("a");
323 string_tree* x = parse_property_list(in);
324 CHECK(x->atom);
325 delete x;
326 }
327 void test_parse_property_list_list() {
328 istringstream in("a:b");
329 string_tree* x = parse_property_list(in);
330 CHECK(!x->atom);
331 CHECK(x->left->atom);
332 CHECK_EQ(x->left->value, "a");
333 CHECK(!x->right->atom);
334 CHECK(x->right->left->atom);
335 CHECK_EQ(x->right->left->value, "b");
336 CHECK(x->right->right == NULL);
337 delete x;
338 }
339
340 :(code)
341 type_tree* new_type_tree(const string_tree* properties) {
342 if (!properties) return NULL;
343 if (properties->atom) {
344 const string& type_name = properties->value;
345 int value = 0;
346 if (contains_key(Type_ordinal, type_name))
347 value = get(Type_ordinal, type_name);
348 else if (is_integer(type_name))
349 value = 0;
350 else if (properties->value == "->")
351 value = 0;
352 else
353 value = -1;
354 return new type_tree(type_name, value);
355 }
356 return new type_tree(new_type_tree(properties->left),
357 new_type_tree(properties->right));
358 }
359
360
361
362 reagent::reagent(const reagent& other) {
363 original_string = other.original_string;
364 name = other.name;
365 value = other.value;
366 initialized = other.initialized;
367 for (int i = 0; i < SIZE(other.properties); ++i) {
368 properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, copy(other.properties.at(i).second)));
369 }
370 type = copy(other.type);
371
372 }
373
374 type_tree::type_tree(const type_tree& original) {
375 atom = original.atom;
376 name = original.name;
377 value = original.value;
378 left = copy(original.left);
379 right = copy(original.right);
380 }
381
382 type_tree& type_tree::operator=(const type_tree& original) {
383 atom = original.atom;
384 name = original.name;
385 value = original.value;
386 if (left) delete left;
387 left = copy(original.left);
388 if (right) delete right;
389 right = copy(original.right);
390 return *this;
391 }
392
393 bool type_tree::operator==(const type_tree& other) const {
394 if (atom != other.atom) return false;
395 if (atom)
396 return name == other.name && value == other.value;
397 return (left == other.left || *left == *other.left)
398 && (right == other.right || *right == *other.right);
399 }
400
401
402 bool type_tree::operator<(const type_tree& other) const {
403 if (atom != other.atom) return atom > other.atom;
404 if (atom) return name < other.name;
405
406 if (left && !other.left) return false;
407 if (!left && other.left) return true;
408 if (right && !other.right) return false;
409 if (!right && other.right) return true;
410
411
412 if (left == other.left || *left == *other.left) return right && *right < *other.right;
413 if (right == other.right || *right == *other.right) return left && *left < *other.left;
414
415 if ((left == other.right || *left == *other.right)
416 && (right == other.left || *right == *other.left))
417 return *left < *other.left;
418
419
420
421 if (*left < *other.left && *left < *other.right) return true;
422 if (*right < *other.left && *right < *other.right) return true;
423 return false;
424 }
425 :(before "End Unit Tests")
426
427 void test_compare_atom_types() {
428 reagent a("a:address"), b("b:boolean");
429 CHECK(*a.type < *b.type);
430 CHECK(!(*b.type < *a.type));
431 }
432 void test_compare_equal_atom_types() {
433 reagent a("a:address"), b("b:address");
434 CHECK(!(*a.type < *b.type));
435 CHECK(!(*b.type < *a.type));
436 }
437 void test_compare_atom_with_non_atom() {
438 reagent a("a:address:number"), b("b:boolean");
439 CHECK(!(*a.type < *b.type));
440 CHECK(*b.type < *a.type);
441 }
442 void test_compare_lists_with_identical_structure() {
443 reagent a("a:address:address"), b("b:address:boolean");
444 CHECK(*a.type < *b.type);
445 CHECK(!(*b.type < *a.type));
446 }
447 void test_compare_identical_lists() {
448 reagent a("a:address:boolean"), b("b:address:boolean");
449 CHECK(!(*a.type < *b.type));
450 CHECK(!(*b.type < *a.type));
451 }
452 void test_compare_list_with_extra_element() {
453 reagent a("a:address:address"), b("b:address:address:number");
454 CHECK(*a.type < *b.type);
455 CHECK(!(*b.type < *a.type));
456 }
457 void test_compare_list_with_smaller_left_but_larger_right() {
458 reagent a("a:address:number"), b("b:character:array");
459 CHECK(*a.type < *b.type);
460 CHECK(!(*b.type < *a.type));
461 }
462 void test_compare_list_with_smaller_left_but_larger_right_identical_types() {
463 reagent a("a:address:boolean"), b("b:boolean:address");
464 CHECK(*a.type < *b.type);
465 CHECK(!(*b.type < *a.type));
466 }
467
468 :(code)
469 string_tree::string_tree(const string_tree& original) {
470 atom = original.atom;
471 value = original.value;
472 left = copy(original.left);
473 right = copy(original.right);
474 }
475
476 reagent& reagent::operator=(const reagent& other) {
477 original_string = other.original_string;
478 for (int i = 0; i < SIZE(properties); ++i)
479 if (properties.at(i).second) delete properties.at(i).second;
480 properties.clear();
481 for (int i = 0; i < SIZE(other.properties); ++i)
482 properties.push_back(pair<string, string_tree*>(other.properties.at(i).first, copy(other.properties.at(i).second)));
483 name = other.name;
484 value = other.value;
485 initialized = other.initialized;
486 if (type) delete type;
487 type = copy(other.type);
488
489 return *this;
490 }
491
492 reagent::~reagent() {
493 clear();
494 }
495
496 void reagent::clear() {
497 for (int i = 0; i < SIZE(properties); ++i) {
498 if (properties.at(i).second) {
499 delete properties.at(i).second;
500 properties.at(i).second = NULL;
501 }
502 }
503 delete type;
504 type = NULL;
505 }
506 type_tree::~type_tree() {
507 delete left;
508 delete right;
509 }
510 string_tree::~string_tree() {
511 delete left;
512 delete right;
513 }
514
515 void append(type_tree*& base, type_tree* extra) {
516 if (!base) {
517 base = extra;
518 return;
519 }
520 type_tree* curr = base;
521 while (curr->right) curr = curr->right;
522 curr->right = extra;
523 }
524
525 void append(string_tree*& base, string_tree* extra) {
526 if (!base) {
527 base = extra;
528 return;
529 }
530 string_tree* curr = base;
531 while (curr->right) curr = curr->right;
532 curr->right = extra;
533 }
534
535 string slurp_until(istream& in, char delim) {
536 ostringstream out;
537 char c;
538 while (in >> c) {
539 if (c == delim) {
540
541 break;
542 }
543 out << c;
544 }
545 return out.str();
546 }
547
548 bool has_property(const reagent& x, const string& name) {
549 for (int i = 0; i < SIZE(x.properties); ++i) {
550 if (x.properties.at(i).first == name) return true;
551 }
552 return false;
553 }
554
555 string_tree* property(const reagent& r, const string& name) {
556 for (int p = 0; p != SIZE(r.properties); ++p) {
557 if (r.properties.at(p).first == name)
558 return r.properties.at(p).second;
559 }
560 return NULL;
561 }
562
563 string_tree* copy(const string_tree* x) {
564 if (x == NULL) return NULL;
565 return new string_tree(*x);
566 }
567
568 type_tree* copy(const type_tree* x) {
569 if (x == NULL) return NULL;
570 return new type_tree(*x);
571 }
572
573 :(before "End Globals")
574 extern const string Ignore(",");
575 :(code)
576 void skip_whitespace_but_not_newline(istream& in) {
577 while (true) {
578 if (!has_data(in)) break;
579 else if (in.peek() == '\n') break;
580 else if (isspace(in.peek())) in.get();
581 else if (Ignore.find(in.peek()) != string::npos) in.get();
582 else break;
583 }
584 }
585
586 void dump_memory() {
587 for (map<int, double>::iterator p = Memory.begin(); p != Memory.end(); ++p) {
588 cout << p->first << ": " << no_scientific(p->second) << '\n';
589 }
590 }
591
592
593
594
595
596
597
598 string to_string(const recipe& r) {
599 ostringstream out;
600 out << "recipe " << r.name << " [\n";
601 for (int i = 0; i < SIZE(r.steps); ++i)
602 out << " " << to_string(r.steps.at(i)) << '\n';
603 out << "]\n";
604 return out.str();
605 }
606
607 string to_original_string(const recipe& r) {
608 ostringstream out;
609 out << "recipe " << r.name << " [\n";
610 for (int i = 0; i < SIZE(r.steps); ++i)
611 out << " " << to_original_string(r.steps.at(i)) << '\n';
612 out << "]\n";
613 return out.str();
614 }
615
616 string debug_string(const recipe& x) {
617 ostringstream out;
618 out << "- recipe " << x.name << '\n';
619
620 for (int index = 0; index < SIZE(x.steps); ++index) {
621 const instruction& inst = x.steps.at(index);
622 out << "inst: " << to_string(inst) << '\n';
623 out << " ingredients\n";
624 for (int i = 0; i < SIZE(inst.ingredients); ++i)
625 out << " " << debug_string(inst.ingredients.at(i)) << '\n';
626 out << " products\n";
627 for (int i = 0; i < SIZE(inst.products); ++i)
628 out << " " << debug_string(inst.products.at(i)) << '\n';
629 }
630 return out.str();
631 }
632
633 string to_original_string(const instruction& inst) {
634 if (inst.is_label) return inst.label;
635 if (!inst.original_string.empty()) return inst.original_string;
636 ostringstream out;
637 for (int i = 0; i < SIZE(inst.products); ++i) {
638 if (i > 0) out << ", ";
639 out << inst.products.at(i).original_string;
640 }
641 if (!inst.products.empty()) out << " <- ";
642 out << inst.name;
643 if (!inst.ingredients.empty()) out << ' ';
644 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
645 if (i > 0) out << ", ";
646 out << inst.ingredients.at(i).original_string;
647 }
648 return out.str();
649 }
650
651 string to_string(const instruction& inst) {
652 if (inst.is_label) return inst.label;
653 ostringstream out;
654 for (int i = 0; i < SIZE(inst.products); ++i) {
655 if (i > 0) out << ", ";
656 out << to_string(inst.products.at(i));
657 }
658 if (!inst.products.empty()) out << " <- ";
659 out << inst.name << ' ';
660 for (int i = 0; i < SIZE(inst.ingredients); ++i) {
661 if (i > 0) out << ", ";
662 out << to_string(inst.ingredients.at(i));
663 }
664 return out.str();
665 }
666
667 string to_string(const reagent& r) {
668 if (is_dummy(r)) return "_";
669 ostringstream out;
670 out << "{";
671 out << r.name << ": " << names_to_string(r.type);
672 if (!r.properties.empty()) {
673 for (int i = 0; i < SIZE(r.properties); ++i)
674 out << ", \"" << r.properties.at(i).first << "\": " << to_string(r.properties.at(i).second);
675 }
676 out << "}";
677 return out.str();
678 }
679
680
681 bool is_dummy(const reagent& x) {
682 return x.name == "_";
683 }
684
685 string debug_string(const reagent& x) {
686 ostringstream out;
687 out << x.name << ": " << x.value << ' ' << to_string(x.type) << " -- " << to_string(x);
688 return out.str();
689 }
690
691 string to_string(const string_tree* property) {
692 if (!property) return "()";
693 ostringstream out;
694 dump(property, out);
695 return out.str();
696 }
697
698 void dump(const string_tree* x, ostream& out) {
699 if (!x) return;
700 if (x->atom) {
701 out << '"' << x->value << '"';
702 return;
703 }
704 out << '(';
705 const string_tree* curr = x;
706 while (curr && !curr->atom) {
707 dump(curr->left, out);
708 if (curr->right) out << ' ';
709 curr = curr->right;
710 }
711
712 if (curr) {
713 out << ". ";
714 dump(curr, out);
715 }
716 out << ')';
717 }
718
719 string to_string(const type_tree* type) {
720 if (type == NULL) return "()";
721 ostringstream out;
722 dump(type, out);
723 return out.str();
724 }
725
726 void dump(const type_tree* x, ostream& out) {
727 if (!x) return;
728 if (x->atom) {
729 dump(x->value, out);
730 return;
731 }
732 out << '(';
733 const type_tree* curr = x;
734 while (curr && !curr->atom) {
735 dump(curr->left, out);
736 if (curr->right) out << ' ';
737 curr = curr->right;
738 }
739
740 if (curr) {
741 out << ". ";
742 dump(curr, out);
743 }
744 out << ')';
745 }
746
747 void dump(type_ordinal type, ostream& out) {
748 if (contains_key(Type, type))
749 out << get(Type, type).name;
750 else
751 out << "?" << type;
752 }
753
754 string names_to_string(const type_tree* type) {
755 if (type == NULL) return "()";
756 ostringstream out;
757 dump_names(type, out);
758 return out.str();
759 }
760
761 void dump_names(const type_tree* x, ostream& out) {
762 if (!x) return;
763 if (x->atom) {
764 out << '"' << x->name << '"';
765 return;
766 }
767 out << '(';
768 const type_tree* curr = x;
769 while (curr && !curr->atom) {
770 dump_names(curr->left, out);
771 if (curr->right) out << ' ';
772 curr = curr->right;
773 }
774
775 if (curr) {
776 out << ". ";
777 dump_names(curr, out);
778 }
779 out << ')';
780 }
781
782 string names_to_string_without_quotes(const type_tree* type) {
783 if (type == NULL) return "()";
784 ostringstream out;
785 dump_names_without_quotes(type, out);
786 return out.str();
787 }
788
789 void dump_names_without_quotes(const type_tree* x, ostream& out) {
790 if (!x) return;
791 if (x->atom) {
792 out << x->name;
793 return;
794 }
795 out << '(';
796 const type_tree* curr = x;
797 while (curr && !curr->atom) {
798 dump_names_without_quotes(curr->left, out);
799 if (curr->right) out << ' ';
800 curr = curr->right;
801 }
802
803 if (curr) {
804 out << ". ";
805 dump_names_without_quotes(curr, out);
806 }
807 out << ')';
808 }
809
810 bool is_integer(const string& s) {
811 return s.find_first_not_of("0123456789-") == string::npos
812 && s.find_first_of("0123456789") != string::npos
813 && s.find('-', 1) == string::npos;
814 }
815
816 int to_integer(string n) {
817 char* end = NULL;
818
819 int result = strtoll(n.c_str(), &end, 0);
820 if (*end != '\0') cerr << "tried to convert " << n << " to number\n";
821 assert(*end == '\0');
822 return result;
823 }
824
825 void test_is_integer() {
826 CHECK(is_integer("1234"));
827 CHECK(is_integer("-1"));
828 CHECK(!is_integer("234.0"));
829 CHECK(is_integer("-567"));
830 CHECK(!is_integer("89-0"));
831 CHECK(!is_integer("-"));
832 CHECK(!is_integer("1e3"));
833 }
834
835
836
837 :(before "End Types")
838 struct no_scientific {
839 double x;
840 explicit no_scientific(double y) :x(y) {}
841 };
842
843 :(code)
844 ostream& operator<<(ostream& os, no_scientific x) {
845 if (!isfinite(x.x)) {
846
847 os << x.x;
848 return os;
849 }
850 ostringstream tmp;
851
852
853 tmp << std::fixed << x.x;
854 os << trim_floating_point(tmp.str());
855 return os;
856 }
857
858 string trim_floating_point(const string& in) {
859 if (in.empty()) return "";
860 if (in.find('.') == string::npos) return in;
861 int length = SIZE(in);
862 while (length > 1) {
863 if (in.at(length-1) != '0') break;
864 --length;
865 }
866 if (in.at(length-1) == '.') --length;
867 if (length == 0) return "0";
868 return in.substr(0, length);
869 }
870
871 void test_trim_floating_point() {
872 CHECK_EQ(trim_floating_point(""), "");
873 CHECK_EQ(trim_floating_point(".0"), "0");
874 CHECK_EQ(trim_floating_point("1.5000"), "1.5");
875 CHECK_EQ(trim_floating_point("1.000001"), "1.000001");
876 CHECK_EQ(trim_floating_point("23.000000"), "23");
877 CHECK_EQ(trim_floating_point("23.0"), "23");
878 CHECK_EQ(trim_floating_point("23."), "23");
879 CHECK_EQ(trim_floating_point("23"), "23");
880 CHECK_EQ(trim_floating_point("230"), "230");
881 CHECK_EQ(trim_floating_point("3.000000"), "3");
882 CHECK_EQ(trim_floating_point("3.0"), "3");
883 CHECK_EQ(trim_floating_point("3."), "3");
884 CHECK_EQ(trim_floating_point("3"), "3");
885 }
886
887 :(before "End Includes")
888 #include <utility>
889 using std::pair;
890 #include <math.h>