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 }