1 module fluentasserts.core.operations.approximately;
2 
3 import fluentasserts.core.results;
4 import fluentasserts.core.evaluation;
5 import fluentasserts.core.array;
6 import fluentasserts.core.serializers;
7 import fluentasserts.core.operations.contain;
8 
9 import fluentasserts.core.lifecycle;
10 
11 import std.algorithm;
12 import std.array;
13 import std.conv;
14 import std.math;
15 
16 version(unittest) {
17   import fluentasserts.core.expect;
18 }
19 
20 ///
21 IResult[] approximately(ref Evaluation evaluation) @trusted nothrow {
22   IResult[] results = [];
23 
24   evaluation.message.addValue("±");
25   evaluation.message.addValue(evaluation.expectedValue.meta["1"]);
26   evaluation.message.addText(".");
27 
28   real current;
29   real expected;
30   real delta;
31 
32   try {
33     current = evaluation.currentValue.strValue.to!real;
34     expected = evaluation.expectedValue.strValue.to!real;
35     delta = evaluation.expectedValue.meta["1"].to!real;
36   } catch(Exception e) {
37     results ~= new MessageResult("Can't parse the provided arguments!");
38 
39     return results;
40   }
41 
42   string strExpected = evaluation.expectedValue.strValue ~ "±" ~ evaluation.expectedValue.meta["1"];
43   string strCurrent = evaluation.currentValue.strValue;
44 
45   auto result = isClose(current, expected, 0, delta);
46 
47   if(evaluation.isNegated) {
48     result = !result;
49   }
50 
51   if(result) {
52     return [];
53   }
54 
55   if(evaluation.currentValue.typeName != "bool") {
56     evaluation.message.addText(" ");
57     evaluation.message.addValue(strCurrent);
58 
59     if(evaluation.isNegated) {
60       evaluation.message.addText(" is approximately ");
61     } else {
62       evaluation.message.addText(" is not approximately ");
63     }
64 
65     evaluation.message.addValue(strExpected);
66     evaluation.message.addText(".");
67   }
68 
69   try results ~= new ExpectedActualResult((evaluation.isNegated ? "not " : "") ~ strExpected, strCurrent); catch(Exception) {}
70 
71   return results;
72 }
73 
74 ///
75 IResult[] approximatelyList(ref Evaluation evaluation) @trusted nothrow {
76   evaluation.message.addValue("±" ~ evaluation.expectedValue.meta["1"]);
77   evaluation.message.addText(".");
78 
79   double maxRelDiff;
80   real[] testData;
81   real[] expectedPieces;
82 
83   try {
84     testData = evaluation.currentValue.strValue.parseList.cleanString.map!(a => a.to!real).array;
85     expectedPieces = evaluation.expectedValue.strValue.parseList.cleanString.map!(a => a.to!real).array;
86     maxRelDiff = evaluation.expectedValue.meta["1"].to!double;
87   } catch(Exception e) {
88     return [ new MessageResult("Can not perform the assert.") ];
89   }
90 
91   auto comparison = ListComparison!real(testData, expectedPieces, maxRelDiff);
92 
93   auto missing = comparison.missing;
94   auto extra = comparison.extra;
95   auto common = comparison.common;
96 
97   IResult[] results = [];
98 
99   bool allEqual = testData.length == expectedPieces.length;
100 
101   if(allEqual) {
102     foreach(i; 0..testData.length) {
103       allEqual = allEqual && isClose(testData[i], expectedPieces[i], 0, maxRelDiff) && true;
104     }
105   }
106 
107   string strExpected;
108   string strMissing;
109 
110   if(maxRelDiff == 0) {
111     strExpected = evaluation.expectedValue.strValue;
112     try strMissing = missing.length == 0 ? "" : missing.to!string;
113     catch(Exception) {}
114   } else try {
115     strMissing = "[" ~ missing.map!(a => a.to!string ~ "±" ~ maxRelDiff.to!string).join(", ") ~ "]";
116     strExpected = "[" ~ expectedPieces.map!(a => a.to!string ~ "±" ~ maxRelDiff.to!string).join(", ") ~ "]";
117   } catch(Exception) {}
118 
119   if(!evaluation.isNegated) {
120     if(!allEqual) {
121       try results ~= new ExpectedActualResult(strExpected, evaluation.currentValue.strValue);
122       catch(Exception) {}
123 
124       try results ~= new ExtraMissingResult(extra.length == 0 ? "" : extra.to!string, strMissing);
125       catch(Exception) {}
126     }
127   } else {
128     if(allEqual) {
129       try results ~= new ExpectedActualResult("not " ~ strExpected, evaluation.currentValue.strValue);
130       catch(Exception) {}
131     }
132   }
133 
134   return results;
135 }