1 /** Various useful types.
2 
3     Authors:    Lars Tandle Kyllingstad
4     Copyright:  Copyright (c) 2009, Lars T. Kyllingstad. All rights reserved.
5     License:    Boost License 1.0
6 */
7 module scid.types;
8 
9 
10 import std.conv;
11 import std.format;
12 import std.math;
13 import std.string: format;
14 import std.traits;
15 
16 
17 
18 
19 /** Struct containing the result of a calculation, along with
20     the absolute error in that calculation (x ± δx).
21 
22     It is assumed, but not checked, that the error is nonnegative.
23 */
24 struct Result(V, E=V)
25 {
26     /// Result.
27     V value;
28     alias value this;
29 
30     /// Error.
31     E error;
32 
33     invariant() { assert (error >= 0); }
34 
35 
36 
37     Result opUnary(string op)() const pure nothrow
38         if (op == "-" || op == "+")
39     {
40         mixin ("return Result("~op~"value, error);");
41     }
42 
43 
44     /** Operators for Result-Result arithmetic.
45         
46         Note that these operations also perform error calculations, and
47         are thus a bit slower than working directly with the value itself.
48         It is assumed that the error is much smaller than the value, so
49         terms of order O(δx δy) or O(δx²) are ignored.
50 
51         Also note that multiplication and division are only possible when
52         the value type V and the error type E are the same (as is the default).
53 
54         Through the magic of "alias this", Result!(V, E) is implicitly
55         castable to the type V, and thus supports the same operations as V
56         in addition to these.
57 
58     */
59     Result opBinary(string op)(Result rhs) pure nothrow
60         if (op == "+" || op == "-")
61     {
62         auto lhs = this;
63         return lhs.opOpAssign!op(rhs);
64     }
65 
66     /// ditto
67     Result opOpAssign(string op)(Result rhs) pure nothrow
68         if (op == "+" || op == "-")
69     {
70         mixin ("value "~op~"= rhs.value;");
71         error += rhs.error;
72         return this;
73     }
74 
75 
76     static if (is (V==E))
77     {
78         /// ditto
79         Result opBinary(string op)(Result rhs) pure nothrow
80             if (op == "*" || op == "/")
81         {
82             auto lhs = this;
83             return lhs.opOpAssign!op(rhs);
84         }
85 
86         /// ditto
87         Result opOpAssign(string op)(Result rhs) pure nothrow
88             if (op == "*")
89         {
90             value *= rhs.value;
91             error = abs(value*rhs.error) + abs(rhs.value*error);
92             return this;
93         }
94 
95         /// ditto
96         Result opOpAssign(string op)(Result rhs) pure nothrow
97             if (op == "/")
98         {
99             V inv = 1/rhs.value;
100             value *= inv;
101             error = (error + abs(rhs.error*value*inv))*abs(inv);
102             return this;
103         }
104     }
105 
106 
107     /** Get a string representation of the result. */
108     void toString(void delegate(const(char)[]) sink, string formatSpec = "%s")
109         const
110     {
111         formattedWrite(sink, formatSpec, value);
112         sink("\u00B1");
113         formattedWrite(sink, formatSpec, error);
114     }
115 
116     /// ditto
117     string toString(string formatSpec = "%s") const
118     {
119         char[] buf;
120         buf.reserve(100);
121         void app(const(char)[] s) { buf ~= s; }
122         toString(&app, formatSpec);
123         return cast(string) buf;
124     }
125 
126 }
127 
128 
129 unittest
130 {
131     alias Result!(real) rr;
132     alias Result!(real, int) rri;
133 }
134 
135 
136 unittest
137 {
138     auto r1 = Result!double(1.0, 0.1);
139     auto r2 = Result!double(2.0, 0.2);
140 
141     assert (+r1 == r1);
142     
143     auto ra = -r1;
144     assert (ra.value == -1.0);
145     assert (ra.error == 0.1);
146 
147     auto rb = r1 + r2;
148     assert (abs(rb.value - 3.0) < double.epsilon);
149     assert (abs(rb.error - 0.3) < double.epsilon);
150 
151     auto rc = r1 - r2;
152     assert (abs(rc.value + 1.0) < double.epsilon);
153     assert (abs(rc.error - 0.3) < double.epsilon);
154 
155     auto rd = r1 * r2;
156 
157     auto re = r1 / r2;
158 }
159 
160 
161 unittest
162 {
163     auto r1 = Result!double(1.0, 0.1);
164     assert (r1.toString() == "1±0.1");
165 
166     auto r2 = Result!double(0.123456789, 0.00123456789);
167     assert (r2.toString("%.8e") == "1.23456789e-01±1.23456789e-03");
168 }
169 
170 
171 
172 
173 /** An interval [a,b] along the real line, where either endpoint may
174     be infinite.
175 
176     Examples:
177     ---
178     auto i1 = interval(1, 5);
179     assert (i1.length == 4);
180     
181     auto i2 = interval(0, real.infinity);
182     assert (i2.isInfinite);
183     ---
184 */
185 struct Interval(T) if (isSigned!T)
186 {
187     /** The interval endpoints. */
188     T a;
189     T b;        ///ditto
190 
191 
192 pure: // TODO: Mark as nothrow as soon as DMD bug 5191 is fixed (DMD 2.051)
193 
194 
195     /** The length of the interval, defined as b-a. */
196     @property T length() @safe const { return b - a; }
197 
198 
199     /** Determine whether the interval is infinite.  This is true if:
200         $(UL
201             $(LI a is infinite, b is finite)
202             $(LI a is finite, b is infinite)
203             $(LI a and b are infinite, but have opposite sign.)
204         )
205         If T is an integer type, this is always false.
206     */
207     @property bool isInfinite() @trusted const
208     {
209         static if (isFloatingPoint!T) return isInfinity(length);
210         else return false;
211     }
212 
213 
214     /** Determine whether this is an ordered interval, i.e.
215         whether a <= b.
216     */
217     @property bool isOrdered() @safe const { return a <= b; }
218 
219 
220     /** If a > b, swap the endpoints. */
221     void order() @safe
222     {
223         if (a > b)
224         {
225             immutable tmp = a;
226             a = b;
227             b = tmp;
228         }
229     }
230 
231 
232     /** Check whether the value x is contained in the interval,
233         i.e. whether a <= x <= b or b <= x <= a.
234     */
235     bool contains(X)(X x) @safe const
236     {
237         return (a <= x && x <= b) || (b <= x && x <= a);
238     }
239 }
240 
241 
242 ///ditto
243 Interval!(CommonType!(T, U)) interval(T, U)(T a, U b)
244     @safe pure //nothrow
245 {
246     return typeof(return)(a, b);
247 }
248 
249 
250 unittest
251 {
252     auto fin = interval(1, 4);
253     assert (fin.a == 1);
254     assert (fin.b == 4);
255     assert (fin.length == 3);
256     assert (!fin.isInfinite);
257     assert (fin.isOrdered);
258     fin.a = 9;
259     assert (!fin.isOrdered);
260     fin.order();
261     assert (fin.a == 4 && fin.b == 9);
262     assert (fin.isOrdered);
263 
264     auto uin = interval(0.0, real.infinity);
265     assert (uin.isInfinite);
266     assert (uin.isOrdered);
267 }