1 /** 2 * Type-based, i.e. statically checked, _units of measurement. 3 * 4 * A $(I quantity) is a wrapper struct around an arbitrary value type, 5 * parametrized with a certain unit. A $(I unit) is basically little more than 6 * a marker type to keep different kinds of quantities apart, but might 7 * additionally have an associated name and symbol string, and – more 8 * more importantly – define $(I conversions) to other _units. While all 9 * of the possible conversions must be defined statically for type-checking, 10 * arbitrary callables are supported for actually converting the values, which 11 * do not necessarily need to be evauatable at compile time. 12 * 13 * Conversions only happen if explicitly requested and there is no different 14 * internal representation of values – for example $(D 1 * kilo(metre)) is 15 * stored just as 1 in memory, not as 1000 or relative to any other »canonical 16 * unit«. 17 * 18 * On top of the template core of the module, to which units are types only, 19 * a layer making use of »dummy« unit instances with operators defined on them 20 * makes it possible to work with quantities and units in a natural way, such 21 * that the actual unit types never need to be user directly in client code 22 * (see the example below). 23 * 24 * In the design of this module, the explicit concept of $(I dimensions) does 25 * not appear, because it would add a fair amount of complication to both the 26 * interface and the implementation for little benefit. Rather, the notion is 27 * established implicitly by defining conversions between pairs of _units – to 28 * see if two _units share the same dimension, just check for convertibility. 29 * 30 * The $(D std.si) module defines SI prefixes and _units for use with this 31 * module. 32 * 33 * Example: 34 * --- 35 * enum foo = baseUnit!("foo", "f"); 36 * enum bar = scale!(foo, 21, "bar", "b"); 37 * 38 * auto a = 2 * bar; 39 * assert(convert!foo(a) == 42 * foo); 40 * --- 41 * 42 * Todo:$(UL 43 * $(LI Integration with the rest of Phobos ($(D std.datetime), $(D std.math), …)) 44 * $(LI Replace the proof-of-concept unit conversion implementation with an 45 * optimized one – currently some unneeded function calls are generated.) 46 * $(LI For $(D scale)/$(D ScaledUnit), use runtime rational/two longs 47 * instead of double conversion per default, to avoid precision issues?) 48 * $(LI Benchmark quantity operations vs. plain value type operations.) 49 * $(LI Make quantities of the same unit implicitly convertible if the value 50 * types are – e.g. via $(D ImplicitConversionTargets) once multiple 51 * alias this statements are allowed.) 52 * $(LI Are multiple conversion targets for a unit really needed? Disallowing 53 * that would remove some odd corner cases.) 54 * $(LI Just forward $(LREF Quantity) instanciations with unit 55 * $(LREF dimensionless) to the value type altogether to avoid the current 56 * limitations regarding $(D alias this)?) 57 * ) 58 * 59 * License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 60 * Authors: $(WEB klickverbot.at, David Nadlinger) 61 */ 62 module experimental.units; 63 64 /* 65 * General implementation notes: 66 * 67 * In the unit tests, enums and static asserts are used quite frequently to 68 * make sure all of the simple functionality is usable at compile time. In 69 * real code, of course, most of the time stuff would be executed at runtime. 70 */ 71 72 import std.typetuple : AliasSeq, allSatisfy, staticMap; 73 74 @safe: 75 76 /** 77 * Mixin template containing the implementation of the unit instance operators. 78 * 79 * Furthermore, it marks the surrounding type as unit – every unit type has to 80 * mixin this template. 81 * 82 * In addition, a unit type might also define a toString() function returning 83 * a custom unit symbol/name, and a list of $(LREF Conversion)s. The example 84 * shows how a unit type for inches might be defined if $(LREF scale) and 85 * $(LREF ScaledUnit) did not exist (which remove the need to write 86 * boilerplate code like this). 87 * 88 * Example: 89 * --- 90 * struct Inch { 91 * mixin UnitImpl; 92 * 93 * static string toString(UnitString type = UnitString.name) { 94 * final switch (type) { 95 * case UnitString.name: return "inch"; 96 * case UnitString.symbol: return "in"; 97 * } 98 * } 99 * 100 * alias AliasSeq!( 101 * Conversion!(centi(metre), toCm, fromCm) 102 * ) Conversions; 103 * 104 * static V toCm(V)(V v) { 105 * return cast(V)(v * 2.54); 106 * } 107 * static V fromCm(V)(V v) { 108 * return cast(V)(v / 2.54); 109 * } 110 * } 111 * enum inch = Inch.init; // Unit instance to use with abbreviated syntax. 112 * --- 113 * 114 * Note: Two existing units $(D a) and $(D c) can't be retroactively extended 115 * with a direct conversion between them. This is by design, as it would break 116 * D's modularization/encapsulation approach (alternative: use mixin template 117 * for defining conversion functions, then it would be possible to have 118 * different behavior of the conversion function in each module). However, 119 * currently it $(I is) possible to create a third unit $(D b) which is 120 * convertible to both $(D a) and $(D c), and then perform the conversion in 121 * two steps: $(D convert!c(convert!b(1 * a))) 122 */ 123 mixin template UnitImpl() 124 { 125 /** 126 * Multiplication/division of two unit instances, yielding a unit instance 127 * representing the product/quotient unit. 128 * 129 * Example: 130 * --- 131 * enum joule = newton * metre; 132 * enum watt = joule / second; 133 * --- 134 */ 135 auto opBinary(string op : "*", Rhs)(Rhs rhs) 136 if (isUnit!Rhs) 137 { 138 return ProductUnit!(typeof(this), Rhs).Result.init; 139 } 140 141 /// ditto 142 auto opBinary(string op : "/", Rhs)(Rhs rhs) 143 if (isUnit!Rhs) 144 { 145 return QuotientUnit!(typeof(this), Rhs).Result.init; 146 } 147 148 /** 149 * Multiplication/division of an unit and a value type, constructing a 150 * `Quantity` instance. 151 * 152 * Example: 153 * --- 154 * auto a = 2 * metre; 155 * auto b = 2 / metre; 156 * auto c = metre * 2; 157 * auto d = metre / 2; 158 * --- 159 */ 160 auto opBinary(string op : "*", V)(V rhs) 161 if (!(isUnit!V || isQuantity!V)) 162 { 163 return Quantity!(typeof(this), V).fromValue(rhs); 164 } 165 166 /// ditto 167 auto opBinary(string op : "/", V)(V rhs) 168 if (!(isUnit!V || isQuantity!V)) 169 { 170 // We cannot just do rhs ^^ -1 because integer types cannot be raised 171 // to negative powers. 172 return Quantity!(typeof(this), V).fromValue((rhs ^^ 0) / rhs); 173 } 174 175 /// ditto 176 auto opBinaryRight(string op : "*", V)(V lhs) 177 if (!(isUnit!V || isQuantity!V)) 178 { 179 return Quantity!(typeof(this), V).fromValue(lhs); 180 } 181 182 /// ditto 183 auto opBinaryRight(string op : "/", V)(V lhs) 184 if (!(isUnit!V || isQuantity!V)) 185 { 186 return Quantity!(PowerUnit!(typeof(this), Rational!(-1)), V).fromValue(lhs); 187 } 188 189 alias SuperSecretAliasToMarkThisAsUnit = void; // See isUnit(T) below. 190 } 191 192 /** 193 * Possible string representations of units. 194 */ 195 enum UnitString 196 { 197 name, /// Use full unit names when constructing strings. 198 symbol /// Use unit symbols when constructing strings. 199 } 200 201 private 202 { 203 // KLUDGE: To avoid operator overloading ambiguities due to the 204 // opBinaryRight definitions in the dummy unit instances and Quantity, 205 // we need to know whether a type is a unit or not. 206 // Another solution would be to limit the possible unit types to 207 // BaseUnit/DerivedUnit/… instances, but this is not really desirable, 208 // especially because units with large custom type conversion tables 209 // are often clearer as custom structs. 210 enum isUnit(T) = __traits(compiles, T.SuperSecretAliasToMarkThisAsUnit); 211 212 template isUnitInstance(alias T) 213 { 214 static if (!__traits(compiles, isUnit!T)) 215 { 216 enum isUnitInstance = isUnit!(typeof(T)); 217 } 218 else static if (!isUnit!T) 219 { 220 enum isUnitInstance = isUnit!(typeof(T)); 221 } 222 else 223 { 224 enum isUnitInstance = false; 225 } 226 } 227 228 /* 229 * Returns a string for the unit U, falling back to stringof if U doesn't 230 * have toString() defined. 231 * 232 * If ct is given, the result is guaranteed to be evaluatable at compile 233 * time (for use in error messages). In theory, this should not be 234 * necessary, as all of the toString functions in here should be CTFE'able, 235 * but due to a @@BUG@@ not yet tracked down, the ones for `ScaledUnit` 236 * and AffineUnit are not (maybe related to the alias parameters?). 237 */ 238 string getUnitString(U, bool ct = false)(UnitString type = UnitString.name) 239 { 240 static if (__traits(compiles, { enum a = U.toString(UnitString.init); })) 241 { 242 return U.toString(type); 243 } 244 else static if (!ct && __traits(compiles, { auto a = U.toString(UnitString.init); })) 245 { 246 return U.toString(type); 247 } 248 else static if (__traits(compiles, { enum a = U.toString(); })) 249 { 250 return U.toString(); 251 } 252 else static if (!ct && __traits(compiles, { auto a = U.toString(); })) 253 { 254 return U.toString(); 255 } 256 else 257 { 258 return U.stringof; 259 } 260 } 261 } 262 263 /** 264 * Shordhand for creating a basic unit with a name and a symbol, and no 265 * conversions defined. 266 * 267 * When using `BaseUnit`, in virtually every use case, you also want to define 268 * an associated unit instance, as shown below. As there should be no real use 269 * for the unit type in user case anyway, you can also use baseUnit which 270 * directly returns a unit instance. 271 * 272 * Example: 273 * --- 274 * alias BaseUnit!("Ampere", "A") Ampere; 275 * enum ampere = Ampere.init; 276 * // or 277 * enum ampere = baseUnit!("Ampere", "A"); 278 * --- 279 */ 280 struct BaseUnit(string name, string symbol = null) 281 { 282 mixin UnitImpl; 283 static string toString(UnitString type = UnitString.name) 284 { 285 switch (type) 286 { 287 case UnitString.symbol: 288 if (symbol) 289 return symbol; 290 default: 291 return name; 292 } 293 } 294 } 295 296 /// ditto 297 enum baseUnit(string name, string symbol = null) = (BaseUnit!(name, symbol)).init; 298 299 version (unittest) private 300 { 301 // Some types for use as base units in unit tests. 302 alias Foo = BaseUnit!("foo", "f"); 303 enum foo = Foo.init; 304 static assert(baseUnit!("foo", "f") == foo); 305 306 alias Bar = BaseUnit!("bar", "br"); 307 enum bar = Bar.init; 308 309 alias Baz = BaseUnit!("baz", "bz"); 310 enum baz = Baz.init; 311 } 312 313 /** 314 * A special unit used to represent dimensionless quantities. 315 */ 316 struct Dimensionless 317 { 318 mixin UnitImpl; 319 static string toString(UnitString type = UnitString.name) 320 { 321 final switch (type) 322 { 323 case UnitString.name: 324 return "dimensionless"; 325 case UnitString.symbol: 326 return ""; 327 } 328 } 329 } 330 331 enum dimensionless = Dimensionless.init; /// ditto 332 333 /** 334 * A pair of a (base) unit and a compile-time rational exponent. 335 * 336 * Multiple `BaseUnitExps` make up a $(LREF DerivedUnit). 337 */ 338 struct BaseUnitExp(B, R) 339 if (!isDerivedUnit!B && 340 isUnit!B && 341 isRational!R) 342 { 343 alias BaseUnit = B; 344 alias Exp = R; 345 } 346 347 private 348 { 349 template isBaseUnitExp(T) 350 { 351 // We can't fold this into a single expression because the special 352 // is syntax works only inside static ifs. 353 static if (is(T _ : BaseUnitExp!(B, R), B, R)) 354 { 355 enum isBaseUnitExp = true; 356 } 357 else 358 { 359 enum isBaseUnitExp = false; 360 } 361 } 362 363 /// 364 @safe pure nothrow @nogc unittest 365 { 366 static assert(isBaseUnitExp!(BaseUnitExp!(Foo, Rational!1))); 367 static assert(!isBaseUnitExp!(Rational!1)); 368 } 369 } 370 371 /** 372 * Constructs a derived unit, consisting of a number of base units and 373 * associated exponents. 374 * 375 * Usually, constructing unit types using the operators defined on the unit 376 * instances is preferred over using this template directly. 377 * 378 * Internally, derived units are represented as DUnit instances, but never try 379 * to use it directly – the DerivedUnit template performs canonicalization to 380 * ensure that semantically equivalent units share the same underlying types. 381 * Also, the result will not actually be a DUnit in some cases, but rather 382 * Dimensionless or a single base unit without exponent. 383 * 384 * Example: 385 * --- 386 * alias Coulomb = DerivedUnit!( 387 * BaseUnitExp!(Ampere, Rational!1), 388 * BaseUnitExp!(Second, Rational!1)); 389 * enum coulomb = Coulomb.init; 390 * 391 * // In most cases, you would want to just use the operators 392 * // on the unit instances instead: 393 * enum coulomb = ampere * second; 394 * --- 395 */ 396 template DerivedUnit(T...) 397 if (allSatisfy!(isBaseUnitExp, T)) 398 { 399 alias DerivedUnit = MakeDerivedUnit!(T).Result; 400 } 401 402 /// 403 @safe pure nothrow @nogc unittest 404 { 405 alias A = DerivedUnit!(BaseUnitExp!(Foo, Rational!(-2))); 406 static assert(is(A.BaseUnitExps == AliasSeq!(BaseUnitExp!(Foo, 407 Rational!(-2)))), "Basic compound unit construction does not work."); 408 409 static assert(is(DerivedUnit!(BaseUnitExp!(Foo, Rational!2), 410 BaseUnitExp!(Bar, Rational!1), BaseUnitExp!(Baz, Rational!3)) == DerivedUnit!( 411 BaseUnitExp!(Bar, Rational!1), BaseUnitExp!(Baz, Rational!3), 412 BaseUnitExp!(Foo, Rational!2))), "Base unit sorting does not work."); 413 414 // FIXME: Another problem probably related to signed/unsigned integer 415 // literals, uncomment BarBU-related arguments below to see the assert fail. 416 static assert(is(DerivedUnit!(BaseUnitExp!(Foo, Rational!2), 417 BaseUnitExp!(Foo, Rational!1) /+, 418 BaseUnitExp!(Bar, Rational!(-3)), BaseUnitExp!(Bar, Rational!2)+/ 419 ) == DerivedUnit!(BaseUnitExp!(Foo, Rational!3), /+BaseUnitExp!(Bar, Rational!(-1)+/ 420 )), "Base unit joining does not work."); 421 422 static assert(is(DerivedUnit!(BaseUnitExp!(Foo, Rational!2), 423 BaseUnitExp!(Foo, Rational!(-2))) == Dimensionless), 424 "Zero exponent base unit pruning does not work."); 425 426 static assert(is(DerivedUnit!(BaseUnitExp!(Dimensionless, Rational!1), 427 BaseUnitExp!(Foo, Rational!1)) == Foo), 428 "Removing Dimensionless during zero exponent pruning does not work."); 429 430 static assert(is(DerivedUnit!(BaseUnitExp!(Foo, Rational!1)) == Foo), 431 "Demotion of trivial compound units to base units does not work."); 432 } 433 434 private 435 { 436 /// ditto 437 struct DUnit(T...) 438 { 439 alias BaseUnitExps = T; 440 mixin UnitImpl; 441 442 /** 443 * Returns a formatted unit string consisting of base unit names and 444 * exponents. 445 * 446 * As recommended in the SI manual, the units are sorted alphabetically. 447 */ 448 static string toString(UnitString type = UnitString.name) 449 { 450 string[] bueStrings; 451 452 foreach (i, Bue; BaseUnitExps) 453 { 454 immutable n = Bue.Exp.numerator; 455 immutable d = Bue.Exp.denominator; 456 if (n == 0) 457 continue; 458 459 string current = getUnitString!(Bue.BaseUnit)(type); 460 if (n != 1) 461 { 462 current ~= "^"; 463 464 immutable parens = n < 0 || d != 1; 465 if (parens) 466 current ~= "("; 467 468 import std.conv : to; 469 current ~= to!string(n); 470 if (d != 1) 471 { 472 current ~= "/" ~ to!string(d); 473 } 474 475 if (parens) 476 current ~= ")"; 477 } 478 bueStrings ~= current; 479 } 480 481 // std.algorithm.sort() is currently buggy at compile-time, use 482 // our custom makeIndex implementation instead. 483 auto indices = new size_t[bueStrings.length]; 484 makeIndexCtfe(bueStrings, indices); 485 486 string result = bueStrings[indices[0]]; 487 foreach (i; indices[1 .. $]) 488 { 489 result ~= " " ~ bueStrings[i]; 490 } 491 return result; 492 } 493 } 494 495 /* 496 * Returns the canonical unit type for the passed parameters. 497 * 498 * The rest of unit handling code does not care about canonicalization, it 499 * just uses `DerivedUnit` where necessary. 500 */ 501 template MakeDerivedUnit(T...) 502 { 503 static if (T.length == 0) 504 { 505 alias Result = Dimensionless; 506 } 507 else 508 { 509 alias CanonicalBUEs = CanonicalizeBaseUnitExps!T.Result; 510 static if (CanonicalBUEs.length == 0) 511 { 512 alias Result = Dimensionless; 513 } 514 else 515 { 516 alias A = CanonicalBUEs[0]; 517 static if (CanonicalBUEs.length == 1 && is(A.Exp == Rational!1)) 518 { 519 // If we just have a single base unit with exponent 1, 520 // don't construct a DUnit, but just return that base unit 521 // instead. 522 alias Result = A.BaseUnit; 523 } 524 else 525 { 526 alias Result = DUnit!CanonicalBUEs; 527 } 528 } 529 } 530 } 531 532 template CanonicalizeBaseUnitExps(T...) 533 { 534 // Sort the `BaseUnitExp` list by the mangled name of the base unit 535 // types. Just `.mangleof` could be used here as well, but 536 // `mangledName` conveniently is a template already. 537 import std.traits : mangledName; 538 alias GetBU(BUE) = BUE.BaseUnit; 539 540 alias BaseUnitNames = staticMap!(mangledName, staticMap!(GetBU, T)); 541 alias SortedBUEs = IndexedTuple!(makeArgumentIndex(BaseUnitNames), T); 542 543 alias Result = PruneZeroBaseUnitExps!(JoinBaseUnitExps!(SortedBUEs).Result).Result; 544 } 545 546 /* 547 * Takes a `AliasSeq` of `BaseUnitExps` and joins adjacent sets which share 548 * the same `BaseUnit`. 549 * 550 * The resulting `AliasSeq` is accesible via `JoinBaseUnitExps.Result`. 551 */ 552 template JoinBaseUnitExps(T...) 553 { 554 static if (T.length < 2) 555 { 556 alias Result = AliasSeq!T; 557 } 558 else 559 { 560 // Have to alias T[0] and T[1] here because otherwise trying to 561 // access T[0].BaseUnit results in a syntax error. If it 562 // wasn't for that, this could be an eponymous template. Is this 563 // a @@BUG@@? 564 alias A = T[0]; 565 alias B = T[1]; 566 static if (is(A.BaseUnit == B.BaseUnit)) 567 { 568 alias Result = JoinBaseUnitExps!(BaseUnitExp!(A.BaseUnit, Sum!(A.Exp, B.Exp)), 569 T[2 .. $]).Result; 570 } 571 else 572 { 573 alias Result = AliasSeq!(A, JoinBaseUnitExps!(T[1 .. $]).Result); 574 } 575 } 576 } 577 578 /* 579 * Takes a `AliasSeq` of `BaseUnitExps` and removes ones with exponent zero 580 * or unit `Dimensionless` (this can happen if »dimensionless« is used 581 * directly). 582 * 583 * The pruned list is accesible via `PruneZeroBaseUnitExps.Result`. 584 */ 585 template PruneZeroBaseUnitExps(T...) 586 { 587 static if (T.length == 0) 588 { 589 alias Result = AliasSeq!(); 590 } 591 else 592 { 593 alias A = T[0]; 594 static if (A.Exp.numerator == 0 || is(A.BaseUnit == Dimensionless)) 595 { 596 alias Result = PruneZeroBaseUnitExps!(T[1 .. $]).Result; 597 } 598 else 599 { 600 alias Result = AliasSeq!(A, PruneZeroBaseUnitExps!(T[1 .. $]).Result); 601 } 602 } 603 } 604 605 template isDerivedUnit(T) 606 { 607 // Matching DUnit with its `AliasSeq` parameter directly doesn't work, 608 // so take advantage of the fact that we can access `BaseUnitExps` from 609 // outside. 610 static if (is(T.BaseUnitExps) && 611 is(T : DUnit!(T.BaseUnitExps))) 612 { 613 enum isDerivedUnit = true; 614 } 615 else 616 { 617 enum isDerivedUnit = false; 618 } 619 } 620 } 621 622 /** 623 * An affine unit – the most common case being a unit that is related to other 624 * units representing the same physical quantity not by a scale factor, but by 625 * a shift in the zero point. 626 * 627 * This is not a fundamentally new concept, adding a constant offset could 628 * just be implemented in a custom conversion function as well (see the 629 * $(LREF UnitImpl) documentation). However, Quantity is specialized on 630 * affine units such as to only provide operations which make sense for them: 631 * 632 * Informally speaking, an affine space is a vector space which »forgot« its 633 * origin, its elements are points, not vectors. Thus, a quantity of an 634 * affine unit cannot be added to another (as it makes no sense to add two 635 * points), but like vectors can be added to points to yield a new point, a 636 * quantity of the underlying base unit can be. Also, two affine quantities 637 * can be substracted to yield a quantity of the base unit (just as two 638 * points can be substracted to get a vector pointing from one to another). 639 * 640 * The most common example for this are units of temperature like degrees 641 * Celsius or Fahrenheit, as demonstrated below. 642 * 643 * Example: 644 * --- 645 * enum celsius = affine!(273.15, "degrees Celsius", "°C")(kelvin); 646 * auto t = 3.0 * celsius; 647 * t += 1.0 * kelvin; // adding Kelvin is okay 648 * assert(!__traits(compiles, t += 2.0 * celsius)); // adding Celsius is not 649 * writeln(t - 0.0 * celsius); // 4 Kelvin, not degrees Celsius 650 * --- 651 */ 652 struct AffineUnit(BaseUnit, alias toBaseOffset, string name, string symbol = null) if ( 653 isUnit!BaseUnit) 654 { 655 alias LinearBaseUnit = BaseUnit; 656 mixin UnitImpl; 657 658 static string toString(UnitString type = UnitString.name) 659 { 660 switch (type) 661 { 662 case UnitString.symbol: 663 if (symbol) 664 return symbol; 665 default: 666 return name; 667 } 668 } 669 670 alias Conversions = AliasSeq!(Conversion!(BaseUnit, toBase, fromBase)); 671 672 static V toBase(V)(V v) 673 { 674 return cast(V)(v + toBaseOffset); 675 } 676 677 static V fromBase(V)(V v) 678 { 679 return cast(V)(v - toBaseOffset); 680 } 681 } 682 683 /// ditto 684 template AffineUnit(alias baseUnit, alias toBaseOffset, string name, string symbol = null) 685 if (isUnitInstance!baseUnit) 686 { 687 alias AffineUnit = AffineUnit!(typeof(baseUnit), toBaseOffset, name, symbol); 688 } 689 690 /// ditto 691 auto affine(alias toBaseOffset, string name, string symbol = null, U)(U u) if (isUnit!U) 692 { 693 alias New = AffineUnit!(U, toBaseOffset, name, symbol); 694 return New.init; 695 } 696 697 /** 698 * A quantity consisting of a value and an associated unit of measurement. 699 * 700 * The unary plus, unary minus, addition, subtraction, multiplication, 701 * division, comparison, increment and decrement operators are forwarded to 702 * the underlying value type, if the operation is meaningful for the given 703 * unit(s). 704 * 705 * A quantity is only implicitly convertible to the underlying value type 706 * (via $(D alias this)) if it is dimensionless – divide a quantity by its 707 * unit if you want to access the raw value. 708 */ 709 struct Quantity(Unit, ValueType = double) 710 if (isUnit!Unit) 711 { 712 enum unit = Unit.init; /// An instance of Unit. 713 import std.traits : isAssignable; 714 715 /** 716 * Two quantites of the same unit are implicitely convertible on 717 * assignment if the underlying value types are. 718 * 719 * Example: 720 * --- 721 * Quantity!(metre, float) a = 2 * metre; 722 * --- 723 */ 724 this(OtherV)(Quantity!(Unit, OtherV) other) 725 if (isAssignable!(ValueType, OtherV)) 726 { 727 value = other.value; 728 } 729 730 /// ditto 731 ref Quantity opAssign(OtherV)(Quantity!(Unit, OtherV) other) 732 if (isAssignable!(ValueType, OtherV)) 733 { 734 value = other.value; 735 return this; 736 } 737 738 /** 739 * A quantity is castable to another one with the same unit if the value 740 * type can be casted to the new one. 741 * 742 * For converting a quantity to another unit, see $(LREF convert) instead. 743 * 744 * Example: 745 * --- 746 * auto a = 2.0 * metre; 747 * assert(cast(Quantity!(metre, int))a == 2 * metre); 748 * --- 749 */ 750 Quantity!(Unit, NewV) opCast(T : Quantity!(Unit, NewV), NewV)() 751 if (is(typeof(cast(NewV) ValueType.init))) 752 { 753 return Quantity!(Unit, NewV).fromValue(cast(NewV) value); 754 } 755 756 // No way to specialize Quantity on AffineUnit (since it takes an alias 757 // template parameter), so just check if we can access its 758 // LinearBaseUnit here. 759 static if (!is(Unit.LinearBaseUnit BaseUnit)) 760 { 761 /** 762 * Unary plus/minus operators. 763 * 764 * Example: 765 * --- 766 * auto l = 6 * metre; 767 * assert(+l == 6 * metre); 768 * assert(-l == (-6) * metre); 769 * --- 770 */ 771 auto opUnary(string op)() 772 if ((op == "+" || op == "-") && 773 is(typeof(mixin(op ~ "ValueType.init")))) 774 { 775 return Quantity!(Unit, typeof(mixin(op ~ "value"))).fromValue(mixin(op ~ "value")); 776 } 777 778 /** 779 * Prefix increment/decrement operators. 780 * 781 * They are only provided dimensionless quantities, because they are 782 * semantically equivalent to adding the dimensionless quantity 1. 783 */ 784 auto opUnary(string op)() 785 if ((op == "++" || op == "--") && 786 is(Unit == Dimensionless) && 787 is(typeof({ValueType v;return mixin(op ~ "v"); }()))) 788 { 789 return Quantity!(Dimensionless, typeof(mixin(op ~ "value"))).fromValue(mixin(op ~ "value")); 790 } 791 792 /** 793 * Addition/substraction of a quantity with the same unit. 794 * 795 * Example: 796 * --- 797 * auto a = 3 * metre; 798 * auto b = 2 * metre; 799 * assert(a + b == 5 * metre); 800 * a -= b; 801 * assert(a == 1 * metre); 802 * --- 803 */ 804 auto opBinary(string op, RhsV)(Quantity!(Unit, RhsV) rhs) 805 if ((op == "+" || op == "-") && 806 is(typeof(mixin("ValueType.init" ~ op ~ "RhsV.init")))) // TODO use CommonType or similar instead? 807 { 808 return Quantity!(Unit, typeof(mixin("value" ~ op ~ "rhs.value"))).fromValue(mixin("value" ~ op ~ "rhs.value")); 809 } 810 811 /// ditto 812 ref Quantity opOpAssign(string op, RhsV)(Quantity!(Unit, RhsV) rhs) 813 if ((op == "+" || op == "-") && 814 __traits(compiles, { ValueType v; mixin("v" ~ op ~ "= RhsV.init;"); }())) 815 { 816 mixin("value" ~ op ~ "= rhs.value;"); 817 return this; 818 } 819 820 /** 821 * Multplication/division by a plain value (i.e. a dimensionless quantity 822 * not represented by a Quantity instance). 823 * 824 * Example: 825 * --- 826 * auto l = 6 * metre; 827 * assert(l * 2 == 12 * metre); 828 * l /= 2; 829 * assert(l == 3 * metre); 830 * --- 831 */ 832 auto opBinary(string op, T)(T rhs) 833 if ((op == "*" || op == "/") && 834 !isUnit!T && 835 !isQuantity!T && 836 is(typeof(mixin("ValueType.init" ~ op ~ "T.init")))) // TODO use CommonType or similar instead? 837 { 838 return Quantity!(Unit, typeof(mixin("value" ~ op ~ "rhs"))).fromValue(mixin("value " ~ op ~ " rhs")); 839 } 840 841 /// ditto 842 auto opBinaryRight(string op, T)(T lhs) 843 if ((op == "*" || op == "/") && 844 !isUnit!T && 845 !isQuantity!T && 846 is(typeof(mixin("T.init" ~ op ~ "ValueType.init"))))// TODO use CommonType or similar instead? 847 { 848 return Quantity!(Unit, typeof(mixin("lhs" ~ op ~ "value"))).fromValue(mixin("lhs " ~ op ~ " value")); 849 } 850 851 /// ditto 852 ref Quantity opOpAssign(string op, T)(T rhs) 853 if ((op == "*" || op == "/") && 854 !isUnit!T && 855 !isQuantity!T && 856 __traits(compiles, { ValueType v; mixin("v" ~ op ~ "= T.init;"); }())) 857 { 858 mixin("value " ~ op ~ "= rhs;"); 859 return this; 860 } 861 862 /** 863 * Multiplication with a unit instance. 864 * 865 * Returns a quantity with the same value, but the new unit. 866 * 867 * Example: 868 * --- 869 * auto l = 6 * metre; 870 * assert(l * metre == 6 * pow!2(metre)); 871 * --- 872 */ 873 auto opBinary(string op : "*", Rhs)(Rhs rhs) 874 if (isUnit!Rhs) 875 { 876 return Quantity!(ProductUnit!(Unit, Rhs).Result, ValueType).fromValue(value); 877 } 878 879 /// ditto 880 auto opBinaryRight(string op : "*", Lhs)(Lhs lhs) 881 if (isUnit!Lhs) 882 { 883 return Quantity!(ProductUnit!(Lhs, Unit).Result, ValueType).fromValue(value); 884 } 885 886 /** 887 * Division by a unit instance. 888 * 889 * Returns a quantity with the same value, but the new unit. 890 * 891 * Example: 892 * --- 893 * auto l = 6 * metre; 894 * assert(l / metre == 6 * dimensionless); 895 * --- 896 */ 897 auto opBinary(string op : "/", RhsU)(RhsU rhs) 898 if (isUnit!RhsU) 899 { 900 return Quantity!(QuotientUnit!(Unit, RhsU).Result, ValueType).fromValue(value); 901 } 902 903 /// ditto 904 auto opBinaryRight(string op : "/", Lhs)(Lhs rhs) 905 if (isUnit!Lhs) 906 { 907 // We cannot just do value ^^ -1 because integer types cannot be 908 // raised to negative powers. 909 return Quantity!(QuotientUnit!(Lhs, Unit).Result, ValueType).fromValue((value ^^ 0) / value); 910 } 911 912 /** 913 * Multiplication with another quantity. 914 * 915 * Example: 916 * --- 917 * auto w = 3 * metre; 918 * auto h = 2 * metre; 919 * assert(w * h == 12 * pow!2(metre)); 920 * --- 921 */ 922 auto opBinary(string op : "*", RhsU, RhsV)(Quantity!(RhsU, RhsV) rhs) 923 { 924 return Quantity!(ProductUnit!(Unit, RhsU).Result, typeof(value * rhs.value)).fromValue(value * rhs.value); 925 } 926 927 /** 928 * Division by another quantity. 929 * 930 * Example: 931 * --- 932 * auto s = 6 * metre; 933 * auto t = 2 * second; 934 * assert(s / t == 3 * metre / second); 935 * --- 936 */ 937 auto opBinary(string op : "/", RhsU, RhsV)(Quantity!(RhsU, RhsV) rhs) 938 { 939 return Quantity!(QuotientUnit!(Unit, RhsU).Result, typeof(value / rhs.value)).fromValue(value / rhs.value); 940 } 941 } 942 else 943 { 944 /** 945 * Substracts a quantity of the same unit, yielding a quantity of the non- 946 * affine base unit. 947 * 948 * Example: 949 * --- 950 * auto a = 3 * celsius; 951 * auto b = 2 * celsius; 952 * assert(a - b == 1 * kelvin); 953 * --- 954 */ 955 auto opBinary(string op : "-", RhsV)(Quantity!(Unit, RhsV) rhs) 956 if (is(typeof(ValueType.init - RhsV.init))) 957 { 958 return Quantity!(BaseUnit, typeof(value - rhs.value)).fromValue(value - rhs.value); 959 } 960 961 /** 962 * Addition/substraction of a quantity with the linear base unit. 963 * 964 * Example: 965 * --- 966 * auto a = 3 * celsius; 967 * auto b = 2 * kelvin; 968 * assert(a + b == 5 * celsius); 969 * a -= b; 970 * assert(a == 1 * celsius); 971 * --- 972 */ 973 auto opBinary(string op, RhsV)(Quantity!(BaseUnit, RhsV) rhs) 974 if ((op == "+" || op == "-") && 975 is(typeof(mixin("ValueType.init" ~ op ~ "RhsV.init")))) 976 { 977 return Quantity!(Unit, typeof(mixin("value" ~ op ~ "rhs.value"))).fromValue(mixin("value" ~ op ~ "rhs.value")); 978 } 979 980 /// ditto 981 ref Quantity opOpAssign(string op, RhsV)(Quantity!(BaseUnit, RhsV) rhs) 982 if ((op == "+" || op == "-") && 983 __traits(compiles, { ValueType v; mixin("v" ~ op ~ "= RhsV.init;"); }())) 984 { 985 mixin("value" ~ op ~ "= rhs.value;"); 986 return this; 987 } 988 989 /// ditto 990 auto opBinaryRight(string op : "+", RhsV)(Quantity!(BaseUnit, RhsV) lhs) 991 if (is(typeof(ValueType.init + RhsV.init))) 992 { 993 return Quantity!(Unit, typeof(value + lhs.value)).fromValue(value + lhs.value); 994 } 995 } 996 997 /** 998 * Comparison with another quantity of the same unit. 999 * 1000 * Example: 1001 * --- 1002 * auto a = 3 * metre; 1003 * auto b = 4 * metre; 1004 * auto c = 5 * second; 1005 * assert(a != b); 1006 * assert(a < b); 1007 * assert(!__traits(compiles, a != c)); 1008 * assert(!__traits(compiles, a < c)); 1009 * --- 1010 */ 1011 int opEquals(RhsV)(Quantity!(Unit, RhsV) rhs) 1012 if (is(typeof(ValueType.init == RhsV.init) : bool)) 1013 { 1014 return value == rhs.value; 1015 } 1016 1017 /// ditto 1018 auto opCmp(RhsV)(Quantity!(Unit, RhsV) rhs) 1019 if (is(typeof(ValueType.init < RhsV.init) : bool)) 1020 { 1021 static if (__traits(compiles, value - rhs.value)) 1022 { 1023 return value - rhs.value; 1024 } 1025 else 1026 { 1027 return (value == rhs.value) ? 0 : (value < rhs.value ? -1 : 1); 1028 } 1029 } 1030 1031 /** 1032 * Returns a string representation of the quantity, consisting of the 1033 * value and a unit symbol or name. 1034 * 1035 * Example: 1036 * --- 1037 * auto l = 6 * metre / second; 1038 * assert(l.toString() == "6 metre second^(-1)"); 1039 * assert(l.toString(UnitString.symbol) == "6 m s^(-1)"); 1040 * --- 1041 */ 1042 string toString(UnitString type = UnitString.name) 1043 { 1044 import std.conv : to; 1045 return to!string(value) ~ " " ~ getUnitString!Unit(type); 1046 } 1047 1048 static if (is(Unit == Dimensionless)) 1049 { 1050 ValueType rawValue() @property 1051 { 1052 return value; 1053 } 1054 1055 void rawValue(ValueType newValue) @property 1056 { 1057 value = newValue; 1058 } 1059 1060 alias rawValue this; 1061 } 1062 1063 auto ref toValue() // Note: added by nordlow 1064 { 1065 return value; 1066 } 1067 1068 private: 1069 static Quantity fromValue(ValueType value) 1070 { 1071 Quantity q = void; 1072 q.value = value; 1073 return q; 1074 } 1075 1076 ValueType value; 1077 } 1078 1079 /// ditto 1080 template Quantity(alias unit, ValueType = double) 1081 if (isUnitInstance!unit) 1082 { 1083 alias Quantity = Quantity!(typeof(unit), ValueType); 1084 } 1085 1086 /// 1087 @safe pure nothrow @nogc unittest 1088 { 1089 enum f1 = 6 * foo; 1090 static assert(+f1 == f1); 1091 static assert(-f1 == (-6) * foo); 1092 static assert(-f1 == -(6 * foo)); 1093 static assert(f1 + f1 == 12 * foo); 1094 static assert(f1 - f1 == 0 * foo); 1095 static assert({ auto f = 2 * foo; f += 3 * foo; f -= 2 * foo; return f; }() == 3 * foo); 1096 static assert(f1 * 2 == 12 * foo); 1097 static assert(f1 / 3 == 2 * foo); 1098 static assert(2 * f1 == 12 * foo); 1099 static assert(12 / f1 == 2 * foo); 1100 static assert({ auto f = 2 * foo; f *= 12; f /= 3; return f; }() == 8 * foo); 1101 1102 enum f2 = 2 * foo; 1103 static assert(f1 * f2 == 12 * foo.pow!2); 1104 enum b = 2 * bar; 1105 static assert(f1 / b == 3 * foo / bar); 1106 static assert(foo / 2 == 0 * foo); 1107 static assert(foo / (2 * bar) == 0 * foo / bar); 1108 1109 static assert(f1 == 6u * foo); 1110 static assert(f1 != f2); 1111 static assert(f1 > f2); 1112 static assert(!__traits(compiles, f1 != b)); 1113 static assert(!__traits(compiles, f1 < b)); 1114 1115 static assert((f1 / b).toString() == "3 bar^(-1) foo"); 1116 static assert((f1 / b).toString(UnitString.symbol) == "3 br^(-1) f"); 1117 1118 enum int fromDimless = f1 / foo; 1119 1120 // Increment/decrement should only work for dimensionless Quantities. 1121 auto f3 = 1 * foo; 1122 static assert(!__traits(compiles, ++f3)); 1123 static assert(!__traits(compiles, --f3)); 1124 static assert({ auto b = 1 * dimensionless; ++b; return b; }() == 2 * dimensionless); 1125 1126 // Implicit conversion in assignments. 1127 Quantity!(foo, float) k = 5 * foo; 1128 k = 3 * foo; 1129 static assert(cast(Quantity!(foo, int))(2.1 * foo) == 2 * foo); 1130 1131 // Quantities of affine units. 1132 enum fooa = foo.affine!(10, "afoo", "af"); 1133 enum fa1 = 3 * fooa; 1134 static assert(!__traits(compiles, fa1 + fa1)); 1135 static assert(fa1 + f1 == 9 * fooa); 1136 static assert(f1 + fa1 == 9 * fooa); 1137 static assert(fa1 - fa1 == 0 * foo); 1138 static assert(fa1 - f1 == (-3) * fooa); 1139 auto fa2 = 2 * fooa; 1140 static assert(!__traits(compiles, fa2 += fa1)); 1141 static assert(!__traits(compiles, fa2 -= fa1)); 1142 static assert({ auto fa = 2 * fooa; fa += 3 * foo; fa -= 2 * foo; return fa; }() == 3 * fooa); 1143 static assert(fa1.convert!foo == 13 * foo); 1144 } 1145 1146 /** 1147 * Raises a unit instance to a given power. 1148 * 1149 * Because the exponent must be known at compile-time (as the type of the 1150 * result depends on it), overloading the ^^ operator for units. 1151 * 1152 * Example: 1153 * --- 1154 * enum squareMetre = pow!2(metre); 1155 * --- 1156 */ 1157 auto pow(Exp, U)(U u) 1158 if (isUnit!U) 1159 { 1160 return PowerUnit!(U, Exp).init; 1161 } 1162 1163 /// ditto 1164 auto pow(int numerator, uint denominator = 1u, U)(U u) 1165 if (isUnit!U) 1166 { 1167 return pow!(Rational!(numerator, denominator))(u); 1168 } 1169 1170 /** 1171 * Raises a quantity to a given power. 1172 * 1173 * Because the exponent must be known at compile-time (to determine the unit 1174 * of the result), overloading the ^^ operator for quantities is not possible. 1175 * 1176 * Example: 1177 * --- 1178 * auto area = pow!2(5 * metre); 1179 * --- 1180 */ 1181 auto pow(Exp, Q : Quantity!(U, V), U, V)(Q q) 1182 { 1183 return Quantity!(PowerUnit!(U, Exp), V).fromValue(q.value ^^ Exp.value); 1184 } 1185 1186 /// ditto 1187 auto pow(int numerator, uint denominator = 1u, Q : Quantity!(U, V), U, V)(Q q) 1188 { 1189 return pow!(Rational!(numerator, denominator))(q); 1190 } 1191 1192 private 1193 { 1194 template isQuantity(Q) 1195 { 1196 static if (is(Q _ : Quantity!(U, V), U, V)) 1197 { 1198 enum isQuantity = true; 1199 } 1200 else 1201 { 1202 enum isQuantity = false; 1203 } 1204 } 1205 1206 template ProductUnit(Lhs, Rhs) 1207 if (isUnit!Lhs && isUnit!Rhs) 1208 { 1209 static if (isDerivedUnit!Lhs) 1210 { 1211 alias LBUExps = Lhs.BaseUnitExps; 1212 } 1213 else 1214 { 1215 alias LBUExps = BaseUnitExp!(Lhs, Rational!1); 1216 } 1217 static if (isDerivedUnit!Rhs) 1218 { 1219 alias RBUExps = Rhs.BaseUnitExps; 1220 } 1221 else 1222 { 1223 alias RBUExps = BaseUnitExp!(Rhs, Rational!1); 1224 } 1225 1226 // Just concatenate the base unit exp lists and pass them to 1227 // DerivedUnit, it takes care of actually summing the exponents for 1228 // identical base units, etc. 1229 alias Result = DerivedUnit!(LBUExps, RBUExps); 1230 } 1231 1232 template QuotientUnit(Lhs, Rhs) 1233 if (isUnit!Lhs && isUnit!Rhs) 1234 { 1235 // Rewrite lhs / rhs as lhs * rhs^^-1. 1236 alias QuotientUnit = ProductUnit!(Lhs, PowerUnit!(Rhs, Rational!(-1))); 1237 } 1238 1239 template PowerUnit(U, Exp) 1240 if (isUnit!U && isRational!Exp) 1241 { 1242 static if (isDerivedUnit!U) 1243 { 1244 // Multiply all base unit exponents with Exp to get the new list 1245 // of BaseUnitExps. 1246 alias PowerUnit = DerivedUnit!(staticMap!(Curry!(PowerBaseUnitExp, Exp), U.BaseUnitExps)); 1247 } 1248 else 1249 { 1250 alias PowerUnit = DerivedUnit!(BaseUnitExp!(U, Exp)); 1251 } 1252 } 1253 1254 /// 1255 @safe pure nothrow @nogc unittest 1256 { 1257 static assert(is(PowerUnit!(DerivedUnit!(BaseUnitExp!(Foo, 1258 Rational!(-2))), Rational!(1, 4u)) == DerivedUnit!(BaseUnitExp!(Foo, 1259 Rational!(-1, 2u))))); 1260 static assert(is(PowerUnit!(DerivedUnit!(BaseUnitExp!(Foo, 1261 Rational!2), BaseUnitExp!(Bar, Rational!1)), Rational!3) == DerivedUnit!( 1262 BaseUnitExp!(Foo, Rational!6), BaseUnitExp!(Bar, Rational!3)))); 1263 static assert(is(PowerUnit!(DerivedUnit!(BaseUnitExp!(Foo, 1264 Rational!2), BaseUnitExp!(Bar, Rational!1)), Rational!0) == Dimensionless)); 1265 static assert(is(PowerUnit!(Foo, 1266 Rational!2) == DerivedUnit!(BaseUnitExp!(Foo, Rational!2)))); 1267 } 1268 1269 alias PowerBaseUnitExp(Exp, B) = BaseUnitExp!(B.BaseUnit, Product!(B.Exp, Exp)); 1270 } 1271 1272 /** 1273 * A conversion »link« to a target unit, consisting of a callable converting 1274 * a value to the target, and one for converting it back. 1275 * 1276 * Is used in the optional Conversions property of base units, see the 1277 * documentation for $(LREF UnitImpl). 1278 */ 1279 // Is a struct to be usable in static foreach. 1280 struct Conversion(T, alias R, alias I) 1281 if (isUnit!T) 1282 { 1283 alias TargetUnit = T; 1284 alias RegularFunc = R; 1285 alias InverseFunc = I; 1286 } 1287 1288 /// ditto 1289 template Conversion(alias t, alias R, alias I) 1290 if (isUnitInstance!t) 1291 { 1292 alias Conversion = Conversion!(typeof(t), R, I); 1293 } 1294 1295 /** 1296 * Converts a quantity to another unit. 1297 * 1298 * The value type of the resulting quantity will be the same as the original 1299 * one. 1300 * 1301 * Examples: 1302 * --- 1303 * writeln(convert!gram(2 * kilogram)); 1304 * writeln(convert!kilogram(2000 * gram)); 1305 * writeln(convert!(milli(newton))(2 * newton)); 1306 * writeln(convert!(kilo(newton))(2000000 * gram * meter / pow!2(second))); 1307 * writeln(convert!(micro(newton) / pow!2(milli(metre)))(1234.0 * pascal)); 1308 * --- 1309 */ 1310 auto convert(TargetUnit, Q : Quantity!(U, V), U, V)(Q q) 1311 if (isUnit!TargetUnit) 1312 { 1313 alias C = GetConversion!(U, TargetUnit, V); 1314 static if (!__traits(compiles, C.Result)) 1315 { 1316 // Due to a @@BUG@@ not yet tracked down, the error message cannot be 1317 // passed to the static assert – from the spellcheck suggestions, it 1318 // seems as if the argument to static assert was evaluated in the 1319 // scope of some GetConversion instance. 1320 static assert(false, 1321 "Error: No conversion from '" ~ getUnitString!(U, true) ~ 1322 "' to '" ~ getUnitString!(TargetUnit, true) ~ "' could be found."); 1323 } 1324 return Quantity!(TargetUnit, V).fromValue(C.Result(q.value)); 1325 } 1326 1327 /// ditto 1328 auto convert(alias targetUnit, Q : Quantity!(U, V), U, V)(Q q) 1329 if (isUnitInstance!targetUnit) 1330 { 1331 return convert!(typeof(targetUnit))(q); 1332 } 1333 1334 /** 1335 * Checks whether a quantity is convertible to a certain unit. 1336 * 1337 * Examples: 1338 * --- 1339 * assert(isConvertibleTo!gram(2 * kilogram)); 1340 * assert(!isConvertibleTo!metre(2 * kilogram)); 1341 * --- 1342 */ 1343 bool isConvertibleTo(TargetUnit, Q : Quantity!(U, V), U, V)(Q q) 1344 if (isUnit!TargetUnit) 1345 { 1346 return __traits(compiles, GetConversion!(U, TargetUnit, V).Result); 1347 } 1348 1349 // TODO make this work 1350 // enum ConvertibleTo(Q : Quantity!(U, V), U, V, TargetUnit) = __traits(compiles, GetConversion!(U, TargetUnit, V).Result); 1351 1352 /// ditto 1353 bool isConvertibleTo(alias targetUnit, Q : Quantity!(U, V), U, V)(Q q) 1354 if (isUnitInstance!targetUnit) 1355 { 1356 return isConvertibleTo!(typeof(targetUnit))(q); 1357 } 1358 1359 /// 1360 @safe pure nothrow @nogc unittest 1361 { 1362 struct ConvertibleToFoo 1363 { 1364 mixin UnitImpl; 1365 alias Conversions = AliasSeq!(Conversion!(Foo, toFoo, fromFoo)); 1366 static string toString(UnitString type = UnitString.name) 1367 { 1368 final switch (type) 1369 { 1370 case UnitString.name: 1371 return "convertibleToFoo"; 1372 case UnitString.symbol: 1373 return "ctf"; 1374 } 1375 } 1376 1377 static V toFoo(V)(V v) 1378 { 1379 return v * 3; 1380 } 1381 1382 static V fromFoo(V)(V v) 1383 { 1384 return v / 3; 1385 } 1386 } 1387 1388 enum convertibleToFoo = ConvertibleToFoo.init; 1389 1390 // »Regular« conversion. 1391 auto c = 2 * convertibleToFoo; 1392 assert(c.convert!foo == 6 * foo); 1393 1394 // »Inverse« conversion. 1395 auto f = 9 * foo; 1396 assert(f.convert!convertibleToFoo == 3 * convertibleToFoo); 1397 1398 static assert((2 * convertibleToFoo).isConvertibleTo!foo); 1399 static assert(!(2 * convertibleToFoo).isConvertibleTo!bar); 1400 } 1401 1402 private 1403 { 1404 template GetConversion(FromUnit, ToUnit, ValueType, bool inverse = false, bool tryReverse = true) 1405 { 1406 static if (is(FromUnit == ToUnit)) 1407 { 1408 // If the destination has already been reached, just return the 1409 // identity function. 1410 enum Result = function(ValueType v) { return v; }; 1411 } 1412 else 1413 { 1414 // Bring both units into the form of a list of base units and 1415 // exponents (a list with one element for non-derived units). This 1416 // way, the algorithm below can remain generic. 1417 alias FromBUEs = GetBaseUnitExps!FromUnit; 1418 alias ToBUEs = GetBaseUnitExps!ToUnit; 1419 1420 // Try substituting every base unit part of FromUnit (resp. the 1421 // only one for non-derived units) with all of its conversion 1422 // targets and see if it can be converted then, i.e. recursively 1423 // walk down the tree/DAG. (Note that the same was/will be done 1424 // for the now ToUnit in the reverse pass.) This is currently a 1425 // depth first search, and thus may lead to unnecessary 1426 // conversions in cases such as newton -> milli(newton). 1427 1428 template ChildrenSearch(size_t startIndex = 0) 1429 { 1430 alias Current = FromBUEs[startIndex]; 1431 alias ErasedBUEs = AliasSeq!(FromBUEs[0 .. startIndex], FromBUEs[startIndex + 1 .. $]); 1432 static if (__traits(compiles, Current.BaseUnit.Conversions)) 1433 { 1434 template isConvertibleTo(Conv) 1435 { 1436 enum isConvertibleTo = is(typeof(GetConversion!(DerivedUnit!(ErasedBUEs, 1437 staticMap!(Curry!(PowerBaseUnitExp, 1438 Current.Exp), GetBaseUnitExps!(Conv.TargetUnit))), 1439 ToUnit, ValueType, inverse, true).Result(ValueType.init)) 1440 : ValueType); 1441 } 1442 1443 alias Search = staticFind!(isConvertibleTo, Current.BaseUnit.Conversions); 1444 } 1445 1446 static if (is(Search.Result)) 1447 { 1448 alias ChildFunc = GetConversion!(DerivedUnit!(ErasedBUEs, 1449 staticMap!(Curry!(PowerBaseUnitExp, Current.Exp), 1450 GetBaseUnitExps!(Search.Result.TargetUnit))), ToUnit, 1451 ValueType, inverse, true).Result; 1452 import std.functional : compose; 1453 static if (inverse) 1454 { 1455 static if (isExpPositive!Current) 1456 { 1457 alias Result = compose!(PowerConvFunc!(Search.Result.InverseFunc, 1458 Current.Exp), ChildFunc); 1459 } 1460 else 1461 { 1462 alias Exp = Product!(Current.Exp, Rational!(-1)); 1463 alias Result = compose!(PowerConvFunc!(Search.Result.RegularFunc, Exp), ChildFunc); 1464 } 1465 } 1466 else 1467 { 1468 static if (isExpPositive!Current) 1469 { 1470 alias Result = compose!(ChildFunc, PowerConvFunc!(Search.Result.RegularFunc, Current.Exp)); 1471 } 1472 else 1473 { 1474 alias Exp = Product!(Current.Exp, Rational!(-1)); 1475 alias Result = compose!(ChildFunc, PowerConvFunc!(Search.Result.InverseFunc, Exp)); 1476 } 1477 } 1478 } 1479 else static if (startIndex < (FromBUEs.length - 1)) 1480 { 1481 alias Next = ChildrenSearch!(startIndex + 1); 1482 static if (is(typeof(Next.Result(ValueType.init)) : ValueType)) 1483 { 1484 alias Result = Next.Result; 1485 } 1486 } 1487 } 1488 1489 static if (is(typeof(ChildrenSearch!(0).Result(ValueType.init)) : ValueType)) 1490 { 1491 alias Result = ChildrenSearch!(0).Result; 1492 } 1493 else 1494 { 1495 // If going from the target unit backwards hasn't already been 1496 // tried, do it now. 1497 static if (tryReverse) 1498 { 1499 alias Reversed = GetConversion!(ToUnit, FromUnit, ValueType, !inverse, false); 1500 static if (is(typeof(Reversed.Result(ValueType.init)) : ValueType)) 1501 { 1502 alias Result = Reversed.Result; 1503 } 1504 } 1505 } 1506 } 1507 } 1508 1509 template PowerConvFunc(alias func, Exp) 1510 if (isRational!Exp) 1511 { 1512 // @@BUG@@: Why doesn't this assert trigger on negative exponents?! 1513 static assert(Exp.numerator > 0); 1514 static if (Exp.denominator != 1u) 1515 { 1516 // Only require v ^^ to be defined in this special case. 1517 V PowerConvFunc(V)(V v) 1518 { 1519 return func((v ^^ Exp.denominator)) ^^ (1.0 / Exp.denominator); 1520 } 1521 } 1522 else 1523 { 1524 import std.meta : Repeat; 1525 import std.functional : compose; 1526 alias PowerConvFunc = compose!(Repeat!(Exp.numerator, func)); 1527 } 1528 } 1529 1530 /* 1531 * Returns a `AliasSeq` of BaseUnitExps for the given unit. 1532 */ 1533 template GetBaseUnitExps(U) 1534 { 1535 static if (isDerivedUnit!U) 1536 { 1537 alias GetBaseUnitExps = U.BaseUnitExps; 1538 } 1539 else 1540 { 1541 alias GetBaseUnitExps = AliasSeq!(BaseUnitExp!(U, Rational!1)); 1542 } 1543 } 1544 1545 /* 1546 * The sum of all the exponents in a list of BaseUnitExps. 1547 */ 1548 template ExponentSum(BUEs...) 1549 { 1550 static if (BUEs.length == 0) 1551 { 1552 alias ExponentSum = Rational!0; 1553 } 1554 else 1555 { 1556 alias ExponentSum = Sum!(BUEs[0].Exp, ExponentSum!(BUEs[1 .. $])); 1557 } 1558 } 1559 1560 enum isExpPositive(BUE) = (BUE.Exp.numerator > 0); 1561 1562 enum isExpNegative(BUE) = (BUE.Exp.numerator < 0); 1563 } 1564 1565 /** 1566 * Shorthands for defining base units with a single conversion factor to 1567 * another base unit. 1568 * 1569 * The conversion is done by simply multiplying/dividing the value by the 1570 * passed factor, which thus has to be defined for all value types this scaled 1571 * unit is used with. 1572 * 1573 * Note that a generic alias is accepted as scaling factor, which makes it 1574 * possible to use runtime values as scale factors without writing a custom 1575 * unit type. 1576 * 1577 * Example: 1578 * --- 1579 * // The following three lines define the same unit. Most of the time, the 1580 * // third syntax is the preferred one because it directly declares a unit 1581 * // instance. 1582 * alias ScaledUnit!(Metre, 0.0254, "inch", "in") Inch; 1583 * alias ScaledUnit!(metre, 0.0254, "inch", "in") Inch; 1584 * enum inch = scale!(metre, 0.0254, "inch", "in"); 1585 * --- 1586 */ 1587 struct ScaledUnit(BaseUnit, alias toBaseFactor, string name, string symbol = null) 1588 if (isUnit!BaseUnit) 1589 { 1590 mixin UnitImpl; 1591 1592 static string toString(UnitString type = UnitString.name) 1593 { 1594 switch (type) 1595 { 1596 case UnitString.symbol: 1597 if (symbol) 1598 return symbol; 1599 else 1600 goto default; 1601 default: 1602 return name; 1603 } 1604 } 1605 1606 alias Conversions = AliasSeq!(Conversion!(BaseUnit, toBase, fromBase)); 1607 1608 static V toBase(V)(V v) 1609 { 1610 return cast(V)(v * toBaseFactor); // TODO cast needs to be removed here to allow integral toBaseFactor when BaseUnit is floating point 1611 } 1612 1613 static V fromBase(V)(V v) 1614 { 1615 return cast(V)(v / toBaseFactor); 1616 } 1617 } 1618 1619 /// ditto 1620 template ScaledUnit(alias baseUnit, alias toBaseFactor, string name, string symbol = null) 1621 if (isUnitInstance!baseUnit) 1622 { 1623 alias ScaledUnit = ScaledUnit!(typeof(baseUnit), toBaseFactor, name, symbol); 1624 } 1625 1626 /// ditto 1627 template scale(alias baseUnit, alias toBaseFactor, string name, string symbol = null) 1628 if (isUnitInstance!baseUnit) 1629 { 1630 enum scale = ScaledUnit!(baseUnit, toBaseFactor, name, symbol).init; 1631 } 1632 1633 version (unittest) 1634 { 1635 int fooToMile; 1636 } 1637 1638 /// 1639 @safe /+ TODO pure +/ nothrow @nogc unittest 1640 { 1641 // Simple conversions should be possible at compile time. 1642 enum millifoo = scale!(foo, 0.001, "millifoo"); 1643 static assert(convert!millifoo(1 * foo) == 1000 * millifoo); 1644 1645 static assert(is(typeof(scale!(foo, 2, "millifoo")) == 1646 typeof(scale!(foo, 2, "millifoo")))); 1647 1648 // Test using a global variable as conversion factor (possible because 1649 // ScaledUnit takes an alias). 1650 enum mile = scale!(foo, fooToMile, "mile"); 1651 fooToMile = 1852; 1652 assert(convert!Foo(1 * mile) == 1852 * foo); 1653 1654 // Conversion over two hops. This test isn't specific to ScaledUnit. 1655 enum microfoo = scale!(millifoo, 0.001, "microfoo"); 1656 static assert(convert!microfoo(1 * foo) == 1000000 * microfoo); 1657 1658 // Conversion over multiple hops in both directions. If A-->B represents 1659 // a conversion defined at A to B, the chain looks like this: 1660 // MicroFoo-->MilliFoo-->Foo<--KiloFoo<--MegaFoo. 1661 enum kilofoo = scale!(foo, 1000, "kilofoo"); 1662 enum megafoo = scale!(kilofoo, 1000, "megafoo"); 1663 static assert(convert!microfoo(1L * megafoo) == 10L ^^ 12 * microfoo); 1664 } 1665 1666 /** 1667 * A unit with a scaling prefix applied, e.g. $(D kilo(metre)). 1668 * 1669 * There is conceptually no difference between defining a regular conversion 1670 * and prefixing a unit. However, PrefixedUnit automatically generates the 1671 * name of the new unit, and it is able to fold multiple prefixes of the same 1672 * system, e.g. $(D milli(kilo(metre))) to just $(D metre). 1673 */ 1674 struct PrefixedUnit(BaseUnit, int exponent, alias System) 1675 if (isUnit!BaseUnit && 1676 !(isPrefixedUnit!BaseUnit && 1677 (BaseUnit.prefixBase == System.base))) 1678 { 1679 mixin UnitImpl; 1680 alias Conversions = AliasSeq!(Conversion!(BaseUnit, toBase, fromBase)); 1681 1682 static string toString(UnitString type = UnitString.name) 1683 { 1684 string pre; 1685 final switch (type) 1686 { 1687 case UnitString.symbol: 1688 pre = prefix.symbol; 1689 break; 1690 case UnitString.name: 1691 pre = prefix.name; 1692 break; 1693 } 1694 static if (isDerivedUnit!BaseUnit) 1695 { 1696 return pre ~ "<" ~ getUnitString!BaseUnit(type) ~ ">"; 1697 } 1698 else 1699 { 1700 return pre ~ getUnitString!BaseUnit(type); 1701 } 1702 } 1703 1704 // This convoluted way of avoiding negative exponents to D's power operator ^^ 1705 // is used so we can avoid to use floating point numbers (raising integers 1706 // to negative exponents is not defined). Need a better solution though. 1707 static V toBase(V)(V v) 1708 { 1709 static if (exponent < 0) 1710 { 1711 return cast(V)(v / (prefixBase ^^ (-exponent))); 1712 } 1713 else 1714 { 1715 return cast(V)(v * (prefixBase ^^ exponent)); 1716 } 1717 } 1718 1719 static V fromBase(V)(V v) 1720 { 1721 static if (exponent < 0) 1722 { 1723 return cast(V)(v * (prefixBase ^^ (-exponent))); 1724 } 1725 else 1726 { 1727 return cast(V)(v / (prefixBase ^^ exponent)); 1728 } 1729 } 1730 1731 private alias Base = BaseUnit; 1732 private enum prefix = getPrefix!System(exponent); 1733 private enum prefixBase = System.base; 1734 } 1735 1736 /// ditto 1737 template PrefixedUnit(BaseUnit, int exponent, alias System) 1738 if (isPrefixedUnit!BaseUnit && 1739 (BaseUnit.prefixBase == System.base)) 1740 { 1741 static if (exponent + BaseUnit.prefix.exponent == 0) 1742 { 1743 // If the exponents sum up to zero, just return the original unit. 1744 alias PrefixedUnit = BaseUnit.Base; 1745 } 1746 else 1747 { 1748 alias PrefixedUnit = PrefixedUnit!(BaseUnit.Base, exponent + BaseUnit.prefix.exponent, System); 1749 } 1750 } 1751 1752 /// ditto 1753 template PrefixedUnit(alias baseUnit, int exponent, alias System) 1754 if (isUnitInstance!baseUnit) 1755 { 1756 alias PrefixedUnit = PrefixedUnit!(typeof(baseUnit), exponent, System); 1757 } 1758 1759 /** 1760 * A named prefix, part of a $(LREF PrefixSystem). 1761 */ 1762 struct Prefix 1763 { 1764 int exponent; /// The power the prefix represents. 1765 string name; /// The name of the prefix, prepended to unit names. 1766 string symbol; /// The symbol of the prefix, prepended to unit symbols. 1767 1768 string toString() 1769 { 1770 import std.conv : to; 1771 return "(" ~ to!string(exponent) ~ ", " ~ name ~ ", " ~ symbol ~ ")"; 1772 } 1773 } 1774 1775 /** 1776 * A prefix system, used with $(LREF PrefixedUnit). 1777 * 1778 * Use the $(LREF DefinePrefixSystem) mixin to automatically generate a helper 1779 * function like $(D kilo()) for every prefix in the system. 1780 * 1781 * $(D getPrefixes) has to be a parameterless callable returning 1782 * $(LREF Prefix)[]. This scheme is used (instead of directly passing the 1783 * prefix array as a parameter) to reduce code bloat, because delegate 1784 * literals are mangled much shorter than array literals. The effect on the 1785 * binary size is quite strong because the mangled name of a PrefixSystem 1786 * instance is part of every symbol in which a PrefixedUnit is involved. 1787 * 1788 * Example: 1789 * --- 1790 * alias PrefixSystem!(10, { return [ 1791 * Prefix(-3, "milli", "m"), 1792 * Prefix(3, "kilo", "k") 1793 * ]; }) System; 1794 * --- 1795 */ 1796 template PrefixSystem(long systemBase, alias getPrefixes) 1797 if (is(typeof(getPrefixes()) : Prefix[])) 1798 { 1799 enum base = systemBase; 1800 enum prefixes = getPrefixes(); 1801 } 1802 1803 /** 1804 * Shorthand for defining prefix templates like $(D kilo!()). 1805 * 1806 * The created template, accessible via the result property, takes a unit 1807 * instance and applies a prefix from the given list of prefixes to it. 1808 * 1809 * Example: 1810 * --- 1811 * alias PrefixSystem!(10, { return [ 1812 * Prefix(-3, "milli", "m"), 1813 * Prefix(3, "kilo", "k") 1814 * ]; }) System; 1815 * alias prefixTemplate!(-3, System) milli; 1816 * alias prefixTemplate!(3, System) kilo; 1817 * // Use the templates like this: milli!metre, kilo!metre, etc. 1818 * --- 1819 */ 1820 template prefixTemplate(int exponent, alias System) 1821 { 1822 template prefixTemplate(alias u) 1823 if (isUnitInstance!u) 1824 { 1825 enum prefixTemplate = PrefixedUnit!(u, exponent, System).init; 1826 } 1827 } 1828 1829 /** 1830 * Mixin template for creating prefix functions for all the prefixes in a 1831 * prefix system. See $(LREF PrefixedUnit) and $(LREF prefixTemplate). 1832 * 1833 * Example: 1834 * --- 1835 * mixin DefinePrefixSystem!(PrefixSystem!(10, { return [ 1836 * Prefix(-3, "milli", "m"), 1837 * Prefix(3, "kilo", "k") 1838 * ]; }); 1839 * // Use milli!() and kilo!() as usual. 1840 * --- 1841 */ 1842 mixin template DefinePrefixSystem(alias System) 1843 { 1844 mixin({ 1845 import std.conv; 1846 1847 string code; 1848 foreach (p; 1849 System.prefixes) 1850 { 1851 code ~= "alias prefixTemplate!(" ~ to!string(p.exponent) ~ ", System) " ~ p.name ~ ";"; 1852 } 1853 return code; 1854 }()); 1855 } 1856 1857 /// 1858 @safe pure nothrow /+ TODO @nogc +/ unittest 1859 { 1860 alias System = PrefixSystem!(10, { return [Prefix(-6, "micro", "µ"), Prefix(-3, "milli", "m"), Prefix(3, "kilo", "k")]; }); 1861 1862 alias kilo = prefixTemplate!(3, System); 1863 alias milli = prefixTemplate!(-3, System); 1864 1865 // FIXME: For reasons unknown, PrefixedUnit.toString() isn't CTFEable. 1866 enum kilofoo = kilo!foo; 1867 assert(kilofoo.toString() == "kilofoo"); 1868 assert(kilofoo.toString(UnitString.symbol) == "kf"); 1869 1870 static assert(is(typeof(milli!(kilo!foo)) == Foo)); 1871 1872 enum microfoo = milli!(milli!foo); 1873 assert(microfoo.toString() == "microfoo"); 1874 assert(microfoo.toString(UnitString.symbol) == "µf"); 1875 1876 static assert(!__traits(compiles, kilo(kilo(foo))), 1877 "Requesting a prefix not in the table didn't fail."); 1878 1879 assert(convert!Foo(1 * kilo!foo) == 1000 * foo); 1880 1881 enum millifoobar = milli!(foo * bar); 1882 assert(millifoobar.toString() == "milli<bar foo>"); 1883 assert(millifoobar.toString(UnitString.symbol) == "m<br f>"); 1884 } 1885 1886 private 1887 { 1888 template isPrefixedUnit(T) 1889 { 1890 // I can't think of a *clean* way to check whether T is a 1891 // `PrefixedUnit`, so just use a duck typing-like approach (instead 1892 // of relying on the mangled names or such). 1893 static if (__traits(compiles, T.Base)) 1894 { 1895 enum isPrefixedUnit = isUnit!(T.Base) && is(typeof(T.prefixBase) : long); 1896 } 1897 else 1898 { 1899 enum isPrefixedUnit = false; 1900 } 1901 } 1902 1903 Prefix getPrefix(alias T)(int exponent) 1904 { 1905 foreach (t; T.prefixes) 1906 { 1907 if (t.exponent == exponent) 1908 { 1909 return t; 1910 } 1911 } 1912 import std.conv : to; 1913 assert(false, 1914 "Prefix for exponent " ~ to!string(exponent) ~ " not defined in prefix list: " ~ to!string( 1915 T.prefixes)); 1916 } 1917 } 1918 1919 /* 1920 * Various compile-time helper functions/templates not specific to units.base. 1921 */ 1922 private 1923 { 1924 /* 1925 * Curries a given template by tying the first n arguments to particular 1926 * types (where n is the number of types passed to Curry). 1927 * 1928 * Example: 1929 * --- 1930 * struct Foo(T, U, V) {} 1931 * alias Curry!(Foo, A, B) CurriedFoo; 1932 * assert(is(CurriedFoo!(C) == Foo!(A, B, C))); 1933 * --- 1934 * 1935 * TODO move to std.functional? 1936 */ 1937 template Curry(alias Target, T...) 1938 { 1939 template Curry(U...) 1940 { 1941 alias Curry = Target!(T, U); 1942 } 1943 } 1944 1945 /// 1946 @safe pure nothrow @nogc unittest 1947 { 1948 struct Test(T, U, V) {} 1949 alias CurriedTest = Curry!(Test, Foo, Bar); 1950 static assert(is(CurriedTest!Baz == Test!(Foo, Bar, Baz))); 1951 } 1952 1953 /// 1954 @safe pure nothrow @nogc unittest 1955 { 1956 import std.meta : Repeat; 1957 static assert(is(Repeat!(3, int, float) == 1958 AliasSeq!(int, float, int, float, int, float))); 1959 } 1960 1961 /* 1962 * Finds the first item matching the template predicate `F` in the type 1963 * tuple `T`. 1964 * 1965 * If there is an occurence, it is aliased to `Result`, otherwise `Result` 1966 * does not exist. 1967 * 1968 * This really needs a better name, just as staticMap from `std.typetuple` 1969 * does. 1970 * 1971 * TODO move to std.meta? 1972 */ 1973 template staticFind(alias F, T...) 1974 { 1975 static if (T.length > 0) 1976 { 1977 static if (F!(T[0])) 1978 { 1979 alias Result = T[0]; 1980 } 1981 else 1982 { 1983 alias staticFind = staticFind!(F, T[1 .. $]); 1984 } 1985 } 1986 } 1987 1988 /* 1989 * Given an array of indices, creates a new `AliasSeq` from the passed one 1990 * according to the index array. 1991 * 1992 * TODO move to std.meta? 1993 */ 1994 template IndexedTuple(alias I, T...) 1995 { 1996 static if (I.length == 0) 1997 { 1998 alias IndexedTuple = AliasSeq!(); 1999 } 2000 else 2001 { 2002 alias IndexedTuple = AliasSeq!(T[I[0]], IndexedTuple!(I[1 .. $], T)); 2003 } 2004 } 2005 2006 /* 2007 * Creates a sorted index from the arguments. 2008 */ 2009 size_t[] makeArgumentIndex(T...)(T t) 2010 { 2011 T[0][] elements; 2012 foreach (e; t) 2013 { 2014 elements ~= e; 2015 } 2016 2017 auto indices = new size_t[elements.length]; 2018 makeIndexCtfe(elements, indices); 2019 return indices; 2020 } 2021 2022 /// 2023 @safe pure nothrow /+ TODO @nogc +/ unittest 2024 { 2025 assert(makeArgumentIndex("c", "d", "a", "b") == [2, 3, 0, 1]); 2026 } 2027 2028 /* 2029 * Specialized version of `std.algorithm.makeIndex` because that currently 2030 * doesn't work with CTFE. 2031 * 2032 * Even though it is easily possible to make CTFE accept it, it silently 2033 * fails (for reasons not yet traced down). 2034 */ 2035 void makeIndexCtfe(T)(T[] r, ref size_t[] index) 2036 { 2037 assert(r.length == index.length); 2038 if (index.length <= 1) 2039 return; 2040 2041 // Can't use ref foreach here due to @@BUG3835@@. 2042 foreach (i, Unused; index) 2043 { 2044 index[i] = i; 2045 } 2046 2047 // Just a simple insertion sort, but works in CTFE both before and 2048 // after Don's array handling overhaul. 2049 for (size_t i = 1; i < index.length; ++i) 2050 { 2051 auto current = index[i]; 2052 auto j = i; 2053 while (j > 0 && (r[index[j - 1]] > r[current])) 2054 { 2055 index[j] = index[j - 1]; 2056 --j; 2057 } 2058 index[j] = current; 2059 } 2060 } 2061 } 2062 2063 /* 2064 * Compile-time rationals. 2065 */ 2066 import std.math : abs; 2067 import std.numeric : gcd; 2068 2069 /** 2070 * A compile-time rational number. 2071 * 2072 * If you explicitely specify the denominator, be sure to use an *unsigned* 2073 * integer literal (e.g. `2u`) – even though the template accepts only unsigned 2074 * integers anyway, this seems to make a difference. 2075 * 2076 * Note: This was tailored to the specific needs of the units library, and 2077 * isn't optimized at all. 2078 */ 2079 template Rational(int n, uint d = 1u) 2080 { 2081 enum gcd_ = gcd(cast(uint)abs(n), d); 2082 static if (gcd_ > 1) 2083 { 2084 alias Rational = Rational!(n / cast(int)gcd_, 2085 d / gcd_); 2086 } 2087 else 2088 { 2089 struct Rational 2090 { 2091 enum int numerator = n; 2092 enum uint denominator = d; 2093 enum double value = cast(double) n / d; 2094 } 2095 } 2096 } 2097 2098 /// 2099 @safe pure nothrow @nogc unittest 2100 { 2101 static assert(is(Rational!(2, 1u) == Rational!2)); 2102 static assert(is(Rational!(6, 3u) == Rational!2)); 2103 } 2104 2105 template isRational(T) 2106 { 2107 static if (is(typeof(T.numerator) : int) && 2108 is(typeof(T.denominator) : uint) && 2109 is(T : Rational!(T.numerator, T.denominator))) 2110 { 2111 enum isRational = true; 2112 } 2113 else 2114 { 2115 enum isRational = false; 2116 } 2117 } 2118 2119 /// 2120 @safe pure nothrow @nogc unittest 2121 { 2122 static assert(isRational!(Rational!(-2, 1u))); 2123 static assert(!isRational!Foo); 2124 } 2125 2126 /** 2127 * The sum of two compile-time rational numbers. 2128 */ 2129 template Sum(Lhs, Rhs) 2130 if (isRational!Lhs && 2131 isRational!Rhs) 2132 { 2133 alias Sum = Rational!(((Lhs.numerator * Rhs.denominator) + 2134 (Rhs.numerator * Lhs.denominator)), 2135 cast(uint)(Lhs.denominator * Rhs.denominator)); 2136 } 2137 2138 /// 2139 @safe pure nothrow @nogc unittest 2140 { 2141 static assert(is(Sum!(Rational!(1, 3u), Rational!(1, 6u)) == Rational!(1, 2u))); 2142 static assert(is(Sum!(Rational!2, Rational!1) == Rational!3)); 2143 } 2144 2145 /** 2146 * The difference between two compile-time rational numbers. 2147 */ 2148 template Difference(Lhs, Rhs) 2149 if (isRational!Lhs && 2150 isRational!Rhs) 2151 { 2152 alias Difference = Rational!(((Lhs.numerator * Rhs.denominator) - 2153 (Rhs.numerator * Lhs.denominator)), 2154 Lhs.denominator * Rhs.denominator); 2155 } 2156 2157 /// 2158 @safe pure nothrow @nogc unittest 2159 { 2160 static assert(is(Difference!(Rational!(1, 2u), Rational!(1, 3u)) == Rational!(1, 6u))); 2161 static assert(is(Difference!(Rational!3, Rational!1) == Rational!2)); 2162 static assert(is(Difference!(Rational!1, Rational!3) == Rational!(-2))); 2163 } 2164 2165 /** 2166 * The product of two compile-time rational numbers. 2167 */ 2168 template Product(Lhs, Rhs) 2169 if (isRational!Lhs && 2170 isRational!Rhs) 2171 { 2172 alias Product = Rational!(Lhs.numerator * Rhs.numerator, 2173 Lhs.denominator * Rhs.denominator); 2174 } 2175 2176 /// 2177 @safe pure nothrow @nogc unittest 2178 { 2179 static assert(is(Product!(Rational!(3, 2u), Rational!(2, 3u)) == Rational!1)); 2180 }