• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2016-2018 T. Zachary Laine
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 #include <boost/yap/expression.hpp>
7 
8 #include <boost/test/minimal.hpp>
9 
10 
11 template<typename T>
12 using term = boost::yap::terminal<boost::yap::expression, T>;
13 
14 template<typename T>
15 using ref = boost::yap::expression_ref<boost::yap::expression, T>;
16 
17 namespace yap = boost::yap;
18 namespace bh = boost::hana;
19 
20 
21 namespace user {
22 
23     struct number
24     {
25         double value;
26 
operator +(number lhs,number rhs)27         friend number operator+(number lhs, number rhs)
28         {
29             return number{lhs.value + rhs.value};
30         }
31 
operator -(number lhs,number rhs)32         friend number operator-(number lhs, number rhs)
33         {
34             return number{lhs.value - rhs.value};
35         }
36 
operator *(number lhs,number rhs)37         friend number operator*(number lhs, number rhs)
38         {
39             return number{lhs.value * rhs.value};
40         }
41 
operator -(number n)42         friend number operator-(number n) { return number{-n.value}; }
43 
operator <(number lhs,double rhs)44         friend bool operator<(number lhs, double rhs)
45         {
46             return lhs.value < rhs;
47         }
48 
operator <(double lhs,number rhs)49         friend bool operator<(double lhs, number rhs)
50         {
51             return lhs < rhs.value;
52         }
53     };
54 
naxpy(number a,number x,number y)55     number naxpy(number a, number x, number y)
56     {
57         return number{a.value * x.value + y.value + 10.0};
58     }
59 
60     struct empty_xform
61     {};
62 
63     struct eval_xform_tag
64     {
operator ()user::eval_xform_tag65         decltype(auto) operator()(
66             yap::expr_tag<yap::expr_kind::terminal>, user::number const & n)
67         {
68             return n;
69         }
70     };
71 
72     struct eval_xform_expr
73     {
operator ()user::eval_xform_expr74         decltype(auto) operator()(term<user::number> const & expr)
75         {
76             return ::boost::yap::value(expr);
77         }
78     };
79 
80     struct eval_xform_both
81     {
operator ()user::eval_xform_both82         decltype(auto) operator()(
83             yap::expr_tag<yap::expr_kind::terminal>, user::number const & n)
84         {
85             return n;
86         }
87 
operator ()user::eval_xform_both88         decltype(auto) operator()(term<user::number> const & expr)
89         {
90             throw std::logic_error("Oops!  Picked the wrong overload!");
91             return ::boost::yap::value(expr);
92         }
93     };
94 
95     struct plus_to_minus_xform_tag
96     {
operator ()user::plus_to_minus_xform_tag97         decltype(auto) operator()(
98             yap::expr_tag<yap::expr_kind::plus>,
99             user::number const & lhs,
100             user::number const & rhs)
101         {
102             return yap::make_expression<yap::expr_kind::minus>(
103                 term<user::number>{lhs}, term<user::number>{rhs});
104         }
105     };
106 
107     struct plus_to_minus_xform_expr
108     {
109         template<typename Expr1, typename Expr2>
operator ()user::plus_to_minus_xform_expr110         decltype(auto) operator()(yap::expression<
111                                   yap::expr_kind::plus,
112                                   bh::tuple<Expr1, Expr2>> const & expr)
113         {
114             return yap::make_expression<yap::expr_kind::minus>(
115                 ::boost::yap::left(expr), ::boost::yap::right(expr));
116         }
117     };
118 
119     struct plus_to_minus_xform_both
120     {
operator ()user::plus_to_minus_xform_both121         decltype(auto) operator()(
122             yap::expr_tag<yap::expr_kind::plus>,
123             user::number const & lhs,
124             user::number const & rhs)
125         {
126             return yap::make_expression<yap::expr_kind::minus>(
127                 term<user::number>{lhs}, term<user::number>{rhs});
128         }
129 
130         template<typename Expr1, typename Expr2>
operator ()user::plus_to_minus_xform_both131         decltype(auto) operator()(yap::expression<
132                                   yap::expr_kind::plus,
133                                   bh::tuple<Expr1, Expr2>> const & expr)
134         {
135             throw std::logic_error("Oops!  Picked the wrong overload!");
136             return yap::make_expression<yap::expr_kind::minus>(
137                 ::boost::yap::left(expr), ::boost::yap::right(expr));
138         }
139     };
140 
141     struct term_nonterm_xform_tag
142     {
operator ()user::term_nonterm_xform_tag143         auto operator()(
144             yap::expr_tag<yap::expr_kind::terminal>, user::number const & n)
145         {
146             return yap::make_terminal(n * user::number{2.0});
147         }
148 
operator ()user::term_nonterm_xform_tag149         auto operator()(
150             yap::expr_tag<yap::expr_kind::plus>,
151             user::number const & lhs,
152             user::number const & rhs)
153         {
154             return yap::make_expression<yap::expr_kind::minus>(
155                 yap::transform(::boost::yap::make_terminal(lhs), *this),
156                 yap::transform(::boost::yap::make_terminal(rhs), *this));
157         }
158     };
159 
160     struct term_nonterm_xform_expr
161     {
operator ()user::term_nonterm_xform_expr162         decltype(auto) operator()(term<user::number> const & expr)
163         {
164             return yap::make_terminal(
165                 ::boost::yap::value(expr) * user::number{2.0});
166         }
167 
168         template<typename Expr1, typename Expr2>
operator ()user::term_nonterm_xform_expr169         decltype(auto) operator()(yap::expression<
170                                   yap::expr_kind::plus,
171                                   bh::tuple<Expr1, Expr2>> const & expr)
172         {
173             return yap::make_expression<yap::expr_kind::minus>(
174                 yap::transform(::boost::yap::left(expr), *this),
175                 yap::transform(::boost::yap::right(expr), *this));
176         }
177     };
178 
179     struct term_nonterm_xform_both
180     {
operator ()user::term_nonterm_xform_both181         decltype(auto) operator()(
182             yap::expr_tag<yap::expr_kind::terminal>, user::number const & n)
183         {
184             return yap::make_terminal(n * user::number{2.0});
185         }
186 
operator ()user::term_nonterm_xform_both187         decltype(auto) operator()(term<user::number> const & expr)
188         {
189             return yap::make_terminal(
190                 ::boost::yap::value(expr) * user::number{2.0});
191         }
192 
operator ()user::term_nonterm_xform_both193         decltype(auto) operator()(
194             yap::expr_tag<yap::expr_kind::plus>,
195             user::number const & lhs,
196             user::number const & rhs)
197         {
198             return yap::make_expression<yap::expr_kind::minus>(
199                 yap::transform(::boost::yap::make_terminal(lhs), *this),
200                 yap::transform(::boost::yap::make_terminal(rhs), *this));
201         }
202 
203         template<typename Expr1, typename Expr2>
operator ()user::term_nonterm_xform_both204         decltype(auto) operator()(yap::expression<
205                                   yap::expr_kind::plus,
206                                   bh::tuple<Expr1, Expr2>> const & expr)
207         {
208             return yap::make_expression<yap::expr_kind::minus>(
209                 yap::transform(::boost::yap::left(expr), *this),
210                 yap::transform(::boost::yap::right(expr), *this));
211         }
212     };
213 
214     struct eval_term_nonterm_xform_tag
215     {
operator ()user::eval_term_nonterm_xform_tag216         decltype(auto) operator()(
217             yap::expr_tag<yap::expr_kind::terminal>, user::number const & n)
218         {
219             return n * user::number{2.0};
220         }
221 
222         template<typename Expr1, typename Expr2>
operator ()user::eval_term_nonterm_xform_tag223         decltype(auto) operator()(
224             yap::expr_tag<yap::expr_kind::plus>,
225             Expr1 const & lhs,
226             Expr2 const & rhs)
227         {
228             return boost::yap::transform(::boost::yap::as_expr(lhs), *this) -
229                    boost::yap::transform(::boost::yap::as_expr(rhs), *this);
230         }
231     };
232 
233     struct eval_term_nonterm_xform_expr
234     {
operator ()user::eval_term_nonterm_xform_expr235         decltype(auto) operator()(term<user::number> const & expr)
236         {
237             return ::boost::yap::value(expr) * user::number{2.0};
238         }
239 
240         template<typename Expr1, typename Expr2>
operator ()user::eval_term_nonterm_xform_expr241         decltype(auto) operator()(yap::expression<
242                                   yap::expr_kind::plus,
243                                   bh::tuple<Expr1, Expr2>> const & expr)
244         {
245             return boost::yap::transform(::boost::yap::left(expr), *this) -
246                    boost::yap::transform(::boost::yap::right(expr), *this);
247         }
248     };
249 
250     struct eval_term_nonterm_xform_both
251     {
operator ()user::eval_term_nonterm_xform_both252         decltype(auto) operator()(
253             yap::expr_tag<yap::expr_kind::terminal>, user::number const & n)
254         {
255             return n * user::number{2.0};
256         }
257 
operator ()user::eval_term_nonterm_xform_both258         decltype(auto) operator()(term<user::number> const & expr)
259         {
260             return ::boost::yap::value(expr) * user::number{2.0};
261         }
262 
263         template<typename Expr1, typename Expr2>
operator ()user::eval_term_nonterm_xform_both264         decltype(auto) operator()(
265             yap::expr_tag<yap::expr_kind::plus>,
266             Expr1 const & lhs,
267             Expr2 const & rhs)
268         {
269             return boost::yap::transform(::boost::yap::as_expr(lhs), *this) -
270                    boost::yap::transform(::boost::yap::as_expr(rhs), *this);
271         }
272 
273         template<typename Expr1, typename Expr2>
operator ()user::eval_term_nonterm_xform_both274         decltype(auto) operator()(yap::expression<
275                                   yap::expr_kind::plus,
276                                   bh::tuple<Expr1, Expr2>> const & expr)
277         {
278             return boost::yap::transform(::boost::yap::left(expr), *this) -
279                    boost::yap::transform(::boost::yap::right(expr), *this);
280         }
281     };
282 
283     decltype(auto)
naxpy_eager_nontemplate_xform(yap::expression<yap::expr_kind::plus,bh::tuple<yap::expression<yap::expr_kind::multiplies,bh::tuple<ref<term<user::number> &>,ref<term<user::number> &>>>,ref<term<user::number> &>>> const & expr)284     naxpy_eager_nontemplate_xform(yap::expression<
285                                   yap::expr_kind::plus,
286                                   bh::tuple<
287                                       yap::expression<
288                                           yap::expr_kind::multiplies,
289                                           bh::tuple<
290                                               ref<term<user::number> &>,
291                                               ref<term<user::number> &>>>,
292                                       ref<term<user::number> &>>> const & expr)
293     {
294         auto a = evaluate(expr.left().left());
295         auto x = evaluate(expr.left().right());
296         auto y = evaluate(expr.right());
297         return yap::make_terminal(naxpy(a, x, y));
298     }
299 
300     decltype(auto)
naxpy_lazy_nontemplate_xform(yap::expression<yap::expr_kind::plus,bh::tuple<yap::expression<yap::expr_kind::multiplies,bh::tuple<ref<term<user::number> &>,ref<term<user::number> &>>>,ref<term<user::number> &>>> const & expr)301     naxpy_lazy_nontemplate_xform(yap::expression<
302                                  yap::expr_kind::plus,
303                                  bh::tuple<
304                                      yap::expression<
305                                          yap::expr_kind::multiplies,
306                                          bh::tuple<
307                                              ref<term<user::number> &>,
308                                              ref<term<user::number> &>>>,
309                                      ref<term<user::number> &>>> const & expr)
310     {
311         decltype(auto) a = expr.left().left().value();
312         decltype(auto) x = expr.left().right().value();
313         decltype(auto) y = expr.right().value();
314         return yap::make_terminal(naxpy)(a, x, y);
315     }
316 
317     struct naxpy_xform
318     {
319         template<typename Expr1, typename Expr2, typename Expr3>
operator ()user::naxpy_xform320         decltype(auto) operator()(yap::expression<
321                                   yap::expr_kind::plus,
322                                   bh::tuple<
323                                       yap::expression<
324                                           yap::expr_kind::multiplies,
325                                           bh::tuple<Expr1, Expr2>>,
326                                       Expr3>> const & expr)
327         {
328             return yap::make_terminal(naxpy)(
329                 transform(expr.left().left(), naxpy_xform{}),
330                 transform(expr.left().right(), naxpy_xform{}),
331                 transform(expr.right(), naxpy_xform{}));
332         }
333     };
334 
335 
336     // unary transforms
337 
338     struct disable_negate_xform_tag
339     {
340         auto
operator ()user::disable_negate_xform_tag341         operator()(yap::expr_tag<yap::expr_kind::negate>, user::number value)
342         {
343             return yap::make_terminal(std::move(value));
344         }
345 
346         template<typename Expr>
347         auto
operator ()user::disable_negate_xform_tag348         operator()(yap::expr_tag<yap::expr_kind::negate>, Expr const & expr)
349         {
350             return expr;
351         }
352     };
353 
354     struct disable_negate_xform_expr
355     {
356         template<typename Expr>
operator ()user::disable_negate_xform_expr357         decltype(auto) operator()(
358             yap::expression<yap::expr_kind::negate, bh::tuple<Expr>> const &
359                 expr)
360         {
361             return ::boost::yap::value(expr);
362         }
363     };
364 
365     struct disable_negate_xform_both
366     {
367         decltype(auto)
operator ()user::disable_negate_xform_both368         operator()(yap::expr_tag<yap::expr_kind::negate>, user::number value)
369         {
370             return yap::make_terminal(std::move(value));
371         }
372 
373         template<typename Expr>
374         decltype(auto)
operator ()user::disable_negate_xform_both375         operator()(yap::expr_tag<yap::expr_kind::negate>, Expr const & expr)
376         {
377             return expr;
378         }
379 
380         template<typename Expr>
operator ()user::disable_negate_xform_both381         decltype(auto) operator()(
382             yap::expression<yap::expr_kind::negate, bh::tuple<Expr>> const &
383                 expr)
384         {
385             throw std::logic_error("Oops!  Picked the wrong overload!");
386             return ::boost::yap::value(expr);
387         }
388     };
389 
390 
391     // ternary transforms
392 
393     //[ tag_xform
394     struct ternary_to_else_xform_tag
395     {
396         template<typename Expr>
operator ()user::ternary_to_else_xform_tag397         decltype(auto) operator()(
398             boost::yap::expr_tag<yap::expr_kind::if_else>,
399             Expr const & cond,
400             user::number then,
401             user::number else_)
402         {
403             return boost::yap::make_terminal(std::move(else_));
404         }
405     };
406     //]
407 
408     //[ expr_xform
409     struct ternary_to_else_xform_expr
410     {
411         template<typename Cond, typename Then, typename Else>
412         decltype(auto)
operator ()user::ternary_to_else_xform_expr413         operator()(boost::yap::expression<
414                    boost::yap::expr_kind::if_else,
415                    boost::hana::tuple<Cond, Then, Else>> const & expr)
416         {
417             return ::boost::yap::else_(expr);
418         }
419     };
420     //]
421 
422     struct ternary_to_else_xform_both
423     {
424         template<typename Expr>
operator ()user::ternary_to_else_xform_both425         decltype(auto) operator()(
426             yap::expr_tag<yap::expr_kind::if_else>,
427             Expr const & cond,
428             user::number then,
429             user::number else_)
430         {
431             return yap::make_terminal(std::move(else_));
432         }
433 
434         template<typename Cond, typename Then, typename Else>
operator ()user::ternary_to_else_xform_both435         decltype(auto) operator()(yap::expression<
436                                   yap::expr_kind::if_else,
437                                   bh::tuple<Cond, Then, Else>> const & expr)
438         {
439             throw std::logic_error("Oops!  Picked the wrong overload!");
440             return ::boost::yap::else_(expr);
441         }
442     };
443 }
444 
double_to_float(term<double> expr)445 auto double_to_float(term<double> expr)
446 {
447     return term<float>{(float)expr.value()};
448 }
449 
check_unique_ptrs_equal_7(term<std::unique_ptr<int>> && expr)450 auto check_unique_ptrs_equal_7(term<std::unique_ptr<int>> && expr)
451 {
452     using namespace boost::hana::literals;
453     BOOST_CHECK(*expr.elements[0_c] == 7);
454     return std::move(expr);
455 }
456 
test_main(int,char * [])457 int test_main(int, char * [])
458 {
459     {
460         term<user::number> a{{1.0}};
461         term<user::number> x{{42.0}};
462         term<user::number> y{{3.0}};
463 
464         {
465             auto expr = a;
466             {
467                 user::number result = evaluate(expr);
468                 BOOST_CHECK(result.value == 1);
469             }
470 
471             {
472                 auto transformed_expr = transform(expr, user::empty_xform{});
473                 user::number result = evaluate(transformed_expr);
474                 BOOST_CHECK(result.value == 1);
475             }
476 
477             {
478                 auto transformed_expr = transform(expr, user::eval_xform_tag{});
479                 BOOST_CHECK(transformed_expr.value == 1);
480             }
481 
482             {
483                 auto transformed_expr =
484                     transform(expr, user::eval_xform_expr{});
485                 BOOST_CHECK(transformed_expr.value == 1);
486             }
487 
488             {
489                 auto transformed_expr =
490                     transform(expr, user::eval_xform_both{});
491                 BOOST_CHECK(transformed_expr.value == 1);
492             }
493         }
494 
495         {
496             auto expr = x + y;
497             {
498                 user::number result = evaluate(expr);
499                 BOOST_CHECK(result.value == 45);
500             }
501 
502             {
503                 auto transformed_expr =
504                     transform(expr, user::plus_to_minus_xform_tag{});
505                 user::number result = evaluate(transformed_expr);
506                 BOOST_CHECK(result.value == 39);
507             }
508 
509             {
510                 auto transformed_expr =
511                     transform(expr, user::plus_to_minus_xform_expr{});
512                 user::number result = evaluate(transformed_expr);
513                 BOOST_CHECK(result.value == 39);
514             }
515 
516             {
517                 auto transformed_expr =
518                     transform(expr, user::plus_to_minus_xform_both{});
519                 user::number result = evaluate(transformed_expr);
520                 BOOST_CHECK(result.value == 39);
521             }
522         }
523 
524         {
525             auto expr = x + user::number{3.0};
526             {
527                 user::number result = evaluate(expr);
528                 BOOST_CHECK(result.value == 45);
529             }
530 
531             {
532                 auto transformed_expr =
533                     transform(expr, user::term_nonterm_xform_tag{});
534                 user::number result = evaluate(transformed_expr);
535                 BOOST_CHECK(result.value == 39 * 2);
536             }
537 
538             {
539                 auto transformed_expr =
540                     transform(expr, user::term_nonterm_xform_expr{});
541                 user::number result = evaluate(transformed_expr);
542                 BOOST_CHECK(result.value == 39 * 2);
543             }
544 
545             {
546                 auto transformed_expr =
547                     transform(expr, user::term_nonterm_xform_both{});
548                 user::number result = evaluate(transformed_expr);
549                 BOOST_CHECK(result.value == 39 * 2);
550             }
551         }
552 
553         {
554             auto expr = x + y;
555             {
556                 user::number result = evaluate(expr);
557                 BOOST_CHECK(result.value == 45);
558             }
559 
560             {
561                 auto transformed_expr =
562                     transform(expr, user::term_nonterm_xform_tag{});
563                 user::number result = evaluate(transformed_expr);
564                 BOOST_CHECK(result.value == 39 * 2);
565             }
566 
567             {
568                 auto transformed_expr =
569                     transform(expr, user::term_nonterm_xform_expr{});
570                 user::number result = evaluate(transformed_expr);
571                 BOOST_CHECK(result.value == 39 * 2);
572             }
573 
574             {
575                 auto transformed_expr =
576                     transform(expr, user::term_nonterm_xform_both{});
577                 user::number result = evaluate(transformed_expr);
578                 BOOST_CHECK(result.value == 39 * 2);
579             }
580         }
581 
582         {
583             auto expr = (x + y) + user::number{1.0};
584             {
585                 user::number result = evaluate(expr);
586                 BOOST_CHECK(result.value == 46);
587             }
588 
589             {
590                 // Differs from those below, because it matches terminals, not
591                 // expressions.
592                 auto transformed_expr =
593                     transform(expr, user::term_nonterm_xform_tag{});
594                 user::number result = evaluate(transformed_expr);
595                 BOOST_CHECK(result.value == 40 * 2);
596             }
597 
598             {
599                 auto transformed_expr =
600                     transform(expr, user::term_nonterm_xform_expr{});
601                 user::number result = evaluate(transformed_expr);
602                 BOOST_CHECK(result.value == 38 * 2);
603             }
604 
605             {
606                 auto transformed_expr =
607                     transform(expr, user::term_nonterm_xform_both{});
608                 user::number result = evaluate(transformed_expr);
609                 BOOST_CHECK(result.value == 38 * 2);
610             }
611         }
612 
613         {
614             auto expr = x + user::number{3.0};
615             {
616                 user::number result = evaluate(expr);
617                 BOOST_CHECK(result.value == 45);
618             }
619 
620             {
621                 user::number result =
622                     transform(expr, user::eval_term_nonterm_xform_tag{});
623                 BOOST_CHECK(result.value == 39 * 2);
624             }
625 
626             {
627                 user::number result =
628                     transform(expr, user::eval_term_nonterm_xform_expr{});
629                 BOOST_CHECK(result.value == 39 * 2);
630             }
631 
632             {
633                 user::number result =
634                     transform(expr, user::eval_term_nonterm_xform_both{});
635                 BOOST_CHECK(result.value == 39 * 2);
636             }
637         }
638 
639         {
640             auto expr = x + y;
641             {
642                 user::number result = evaluate(expr);
643                 BOOST_CHECK(result.value == 45);
644             }
645 
646             {
647                 user::number result =
648                     transform(expr, user::eval_term_nonterm_xform_tag{});
649                 BOOST_CHECK(result.value == 39 * 2);
650             }
651 
652             {
653                 user::number result =
654                     transform(expr, user::eval_term_nonterm_xform_expr{});
655                 BOOST_CHECK(result.value == 39 * 2);
656             }
657 
658             {
659                 user::number result =
660                     transform(expr, user::eval_term_nonterm_xform_both{});
661                 BOOST_CHECK(result.value == 39 * 2);
662             }
663         }
664 
665         {
666             auto expr = (x + y) + user::number{1.0};
667             {
668                 user::number result = evaluate(expr);
669                 BOOST_CHECK(result.value == 46);
670             }
671 
672             {
673                 user::number result =
674                     transform(expr, user::eval_term_nonterm_xform_tag{});
675                 BOOST_CHECK(result.value == 38 * 2);
676             }
677 
678             {
679                 user::number result =
680                     transform(expr, user::eval_term_nonterm_xform_expr{});
681                 BOOST_CHECK(result.value == 38 * 2);
682             }
683 
684             {
685                 user::number result =
686                     transform(expr, user::eval_term_nonterm_xform_both{});
687                 BOOST_CHECK(result.value == 38 * 2);
688             }
689         }
690 
691         {
692             auto expr = a * x + y;
693             {
694                 user::number result = evaluate(expr);
695                 BOOST_CHECK(result.value == 45);
696             }
697 
698             auto transformed_expr =
699                 transform(expr, user::naxpy_eager_nontemplate_xform);
700             {
701                 user::number result = evaluate(transformed_expr);
702                 BOOST_CHECK(result.value == 55);
703             }
704         }
705 
706         {
707             auto expr = a + (a * x + y);
708             {
709                 user::number result = evaluate(expr);
710                 BOOST_CHECK(result.value == 46);
711             }
712 
713             auto transformed_expr =
714                 transform(expr, user::naxpy_eager_nontemplate_xform);
715             {
716                 user::number result = evaluate(transformed_expr);
717                 BOOST_CHECK(result.value == 56);
718             }
719         }
720 
721         {
722             auto expr = a * x + y;
723             {
724                 user::number result = evaluate(expr);
725                 BOOST_CHECK(result.value == 45);
726             }
727 
728             auto transformed_expr =
729                 transform(expr, user::naxpy_lazy_nontemplate_xform);
730             {
731                 user::number result = evaluate(transformed_expr);
732                 BOOST_CHECK(result.value == 55);
733             }
734         }
735 
736         {
737             auto expr = a + (a * x + y);
738             {
739                 user::number result = evaluate(expr);
740                 BOOST_CHECK(result.value == 46);
741             }
742 
743             auto transformed_expr =
744                 transform(expr, user::naxpy_lazy_nontemplate_xform);
745             {
746                 user::number result = evaluate(transformed_expr);
747                 BOOST_CHECK(result.value == 56);
748             }
749         }
750 
751         {
752             auto expr = (a * x + y) * (a * x + y) + (a * x + y);
753             {
754                 user::number result = evaluate(expr);
755                 BOOST_CHECK(result.value == 45 * 45 + 45);
756             }
757 
758             auto transformed_expr = transform(expr, user::naxpy_xform{});
759             {
760                 user::number result = evaluate(transformed_expr);
761                 BOOST_CHECK(result.value == 55 * 55 + 55 + 10);
762             }
763         }
764     }
765 
766     {
767         term<double> unity{1.0};
768         term<std::unique_ptr<int>> i{new int{7}};
769         yap::expression<
770             yap::expr_kind::plus,
771             bh::tuple<ref<term<double> &>, term<std::unique_ptr<int>>>>
772             expr_1 = unity + std::move(i);
773 
774         yap::expression<
775             yap::expr_kind::plus,
776             bh::tuple<
777                 ref<term<double> &>,
778                 yap::expression<
779                     yap::expr_kind::plus,
780                     bh::tuple<
781                         ref<term<double> &>,
782                         term<std::unique_ptr<int>>>>>>
783             expr_2 = unity + std::move(expr_1);
784 
785         auto transformed_expr = transform(std::move(expr_2), double_to_float);
786 
787         transform(std::move(transformed_expr), check_unique_ptrs_equal_7);
788     }
789 
790     {
791         term<user::number> a{{1.0}};
792         term<user::number> x{{42.0}};
793         term<user::number> y{{3.0}};
794 
795         {
796             auto expr = -x;
797             {
798                 user::number result = evaluate(expr);
799                 BOOST_CHECK(result.value == -42);
800             }
801 
802             {
803                 auto transformed_expr =
804                     transform(expr, user::disable_negate_xform_tag{});
805                 user::number result = evaluate(transformed_expr);
806                 BOOST_CHECK(result.value == 42);
807             }
808 
809             {
810                 auto transformed_expr =
811                     transform(expr, user::disable_negate_xform_expr{});
812                 user::number result = evaluate(transformed_expr);
813                 BOOST_CHECK(result.value == 42);
814             }
815 
816             {
817                 auto transformed_expr =
818                     transform(expr, user::disable_negate_xform_both{});
819                 user::number result = evaluate(transformed_expr);
820                 BOOST_CHECK(result.value == 42);
821             }
822         }
823 
824         {
825             auto expr = a * -x + y;
826             {
827                 user::number result = evaluate(expr);
828                 BOOST_CHECK(result.value == -39);
829             }
830 
831             {
832                 auto transformed_expr =
833                     transform(expr, user::disable_negate_xform_tag{});
834                 user::number result = evaluate(transformed_expr);
835                 BOOST_CHECK(result.value == 45);
836             }
837 
838             {
839                 auto transformed_expr =
840                     transform(expr, user::disable_negate_xform_expr{});
841                 user::number result = evaluate(transformed_expr);
842                 BOOST_CHECK(result.value == 45);
843             }
844 
845             {
846                 auto transformed_expr =
847                     transform(expr, user::disable_negate_xform_both{});
848                 user::number result = evaluate(transformed_expr);
849                 BOOST_CHECK(result.value == 45);
850             }
851         }
852 
853         {
854             auto expr = -(x + y);
855             {
856                 user::number result = evaluate(expr);
857                 BOOST_CHECK(result.value == -45);
858             }
859 
860             {
861                 auto transformed_expr =
862                     transform(expr, user::disable_negate_xform_tag{});
863                 user::number result = evaluate(transformed_expr);
864                 BOOST_CHECK(result.value == 45);
865             }
866 
867             {
868                 auto transformed_expr =
869                     transform(expr, user::disable_negate_xform_expr{});
870                 user::number result = evaluate(transformed_expr);
871                 BOOST_CHECK(result.value == 45);
872             }
873 
874             {
875                 auto transformed_expr =
876                     transform(expr, user::disable_negate_xform_both{});
877                 user::number result = evaluate(transformed_expr);
878                 BOOST_CHECK(result.value == 45);
879             }
880         }
881     }
882 
883     {
884         term<user::number> a{{1.0}};
885         term<user::number> x{{42.0}};
886         term<user::number> y{{3.0}};
887 
888         {
889             auto expr = if_else(0 < a, x, y);
890             {
891                 user::number result = evaluate(expr);
892                 BOOST_CHECK(result.value == 42);
893             }
894 
895             {
896                 auto transformed_expr =
897                     transform(expr, user::ternary_to_else_xform_tag{});
898                 user::number result = evaluate(transformed_expr);
899                 BOOST_CHECK(result.value == 3);
900             }
901 
902             {
903                 auto transformed_expr =
904                     transform(expr, user::ternary_to_else_xform_expr{});
905                 user::number result = evaluate(transformed_expr);
906                 BOOST_CHECK(result.value == 3);
907             }
908 
909             {
910                 auto transformed_expr =
911                     transform(expr, user::ternary_to_else_xform_both{});
912                 user::number result = evaluate(transformed_expr);
913                 BOOST_CHECK(result.value == 3);
914             }
915         }
916 
917         {
918             auto expr = y * if_else(0 < a, x, y) + user::number{0.0};
919             {
920                 user::number result = evaluate(expr);
921                 BOOST_CHECK(result.value == 126);
922             }
923 
924             {
925                 auto transformed_expr =
926                     transform(expr, user::ternary_to_else_xform_tag{});
927                 user::number result = evaluate(transformed_expr);
928                 BOOST_CHECK(result.value == 9);
929             }
930 
931             {
932                 auto transformed_expr =
933                     transform(expr, user::ternary_to_else_xform_expr{});
934                 user::number result = evaluate(transformed_expr);
935                 BOOST_CHECK(result.value == 9);
936             }
937 
938             {
939                 auto transformed_expr =
940                     transform(expr, user::ternary_to_else_xform_both{});
941                 user::number result = evaluate(transformed_expr);
942                 BOOST_CHECK(result.value == 9);
943             }
944         }
945     }
946 
947     return 0;
948 }
949