1 /*
2  fuzzylite (R), a fuzzy logic control library in C++.
3  Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved.
4  Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com>
5 
6  This file is part of fuzzylite.
7 
8  fuzzylite is free software: you can redistribute it and/or modify it under
9  the terms of the FuzzyLite License included with the software.
10 
11  You should have received a copy of the FuzzyLite License along with
12  fuzzylite. If not, see <http://www.fuzzylite.com/license/>.
13 
14  fuzzylite is a registered trademark of FuzzyLite Limited.
15  */
16 
17 #include "test/catch.hpp"
18 #include "fl/Headers.h"
19 
20 namespace fl {
21 
22     /**
23      * Tests: term/Function
24      *
25      * @author Juan Rada-Vilela, Ph.D.
26      *
27      */
28 
29     TEST_CASE("function parses basic arithmetic", "[term][function]") {
30         Function f;
31         std::string text = "3+4*2/(1-5)^2^3";
32         CHECK(f.toPostfix(text) == "3 4 2 * 1 5 - 2 3 ^ ^ / +");
33         CHECK(f.parse(text)->toInfix() == "3.000 ^ 2.000 ^ 5.000 - 1.000 / 2.000 * 4.000 + 3.000");
34         CHECK(f.parse(text)->toPrefix() == "+ / ^ ^ 3.000 2.000 - 5.000 1.000 * 2.000 4.000 3.000");
35     }
36 
37     TEST_CASE("function parses basic trigonometry", "[term][function]") {
38         Function f;
39         std::string text = "sin(y*x)^2/x";
40 
41         CHECK_THROWS(f.load(text));
42 
43         f.variables["y"] = 1.0;
44         f.load(text);
45 
46         CHECK(f.toPostfix(text) == "y x * sin 2 ^ x /");
47         CHECK(f.parse(text)->toInfix() == "x / 2.000 ^ x * y sin");
48         CHECK(f.parse(text)->toPrefix() == "/ x ^ 2.000 sin * x y");
49     }
50 
51     TEST_CASE("function parses propositions", "[term][function]") {
52         Function f;
53 
54         std::string text = "(Temperature is High and Oxygen is Low) or "
55                 "(Temperature is Low and (Oxygen is Low or Oxygen is High))";
56 
57         CHECK(f.toPostfix(text) == "Temperature is High Oxygen is Low "
58                 "and Temperature is Low Oxygen is Low Oxygen is High or and or");
59     }
60 
61     TEST_CASE("function cannot deal with negative numbers", "[term][function]") {
62         Function f;
63         std::string text = "-5 *4/sin(-pi/2)";
64 
65         SECTION("function throws exception") {
66             CHECK_THROWS(f.parse(text)->evaluate());
67         }
68 
69         f.variables["pi"] = 3.14;
70         CHECK_THROWS(f.parse(text)->evaluate(&f.variables));
71 
72         text = "~5 *4/sin(~pi/2)";
73         CHECK(f.parse(text)->evaluate(&f.variables) == Approx(20));
74 
75         f.load(text);
76 
77         f.variables["pi"] = 3.14;
78 
79         CHECK(f.toPostfix(text) == "5 ~ 4 * pi ~ 2 / sin /");
80         CHECK(f.parse(text)->toInfix() == "2.000 / pi ~ sin / 4.000 * 5.000 ~");
81         CHECK(f.parse(text)->toPrefix() == "/ sin / 2.000 ~ pi * 4.000 ~ 5.000");
82     }
83 
84     TEST_CASE("Function is clonable", "[term][function]") {
85         Function* f = new Function;
86         std::string text = "2+2";
87         f->load(text);
88         CHECK(Op::isEq(f->membership(fl::nan), 4));
89         Function* clone = f->clone();
90         delete f;
91         CHECK(Op::isEq(clone->membership(fl::nan), 4));
92         delete clone;
93     }
94 
95     TEST_CASE("Function is constructor copyable", "[term][function]") {
96         Function* f = new Function;
97         std::string text = "2+2";
98         f->load(text);
99         CHECK(Op::isEq(f->membership(fl::nan), 4));
100         Function* clone = new Function(*f);
101         delete f;
102         CHECK(Op::isEq(clone->membership(fl::nan), 4));
103         delete clone;
104     }
105 
106     TEST_CASE("Function computes tree size correctly", "[term][function]"){
107         Function f("f", "x*x+(x-x)/x+log(x)");
108         f.load();
109         CHECK(f.root()->treeSize() == 6);
110         CHECK(f.root()->treeSize(Function::Element::Function) == 1);
111         CHECK(f.root()->treeSize(Function::Element::Operator) == 5);
112         FL_LOG(f.complexity().toString());
113     }
114 
115 }
116