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 }