1 // Written in the D programming language
2 /++
3 This module defines common math operations on quantities.
4 
5 Copyright: Copyright 2013-2014, Nicolas Sicard
6 Authors: Nicolas Sicard
7 License: $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8 Standards: $(LINK http://www.bipm.org/en/si/si_brochure/)
9 Source: $(LINK https://github.com/biozic/quantities)
10 +/
11 module quantities.math;
12 
13 import quantities.base;
14 
15 /++
16 Mixin template that introduces math functions operating on a quantity of value type N in the
17 current scope. Each function imports module_ internally. This module should
18 contain the math primitives that can operate on the variables of type N, such
19 as sqrt, cbrt, pow and fabs.
20 +/
21 mixin template MathFunctions(N, string module_ = "std.math")
22 {
23     auto square(U)(U unit)
24         if (isQuantity!U && is(U.valueType == N))
25     {
26         return pow!2(unit);
27     }
28 
29     /// ditto
30     auto cubic(U)(U unit)
31         if (isQuantity!U && is(U.valueType == N))
32     {
33         return pow!3(unit);
34     }
35 
36     /// ditto
37     auto pow(int n, U)(U unit)
38         if (isQuantity!U && is(U.valueType == N))
39     {
40         mixin("import " ~ module_ ~ ";");
41         static assert(__traits(compiles, unit.rawValue ^^ n),
42                       U.valueType.stringof ~ " doesn't overload operator ^^");
43         return Quantity!(U.valueType, Pow!(n, U.dimensions)).make(unit.rawValue ^^ n);
44     }
45 
46     /// ditto
47     auto sqrt(Q)(Q quantity)
48         if (isQuantity!Q && is(Q.valueType == N))
49     {
50         mixin("import " ~ module_ ~ ";");
51         static assert(__traits(compiles, sqrt(quantity.rawValue)),
52                       "No overload of sqrt for an argument of type " ~ Q.valueType.stringof);
53         return Quantity!(Q.valueType, PowInverse!(2, Q.dimensions)).make(sqrt(quantity.rawValue));
54     }
55 
56     /// ditto
57     auto cbrt(Q)(Q quantity)
58         if (isQuantity!Q && is(Q.valueType == N))
59     {
60         mixin("import " ~ module_ ~ ";");
61         static assert(__traits(compiles, cbrt(quantity.rawValue)),
62                       "No overload of cbrt for an argument of type " ~ Q.valueType.stringof);
63         return Quantity!(Q.valueType, PowInverse!(3, Q.dimensions)).make(cbrt(quantity.rawValue));
64     }
65 
66     /// ditto
67     auto nthRoot(int n, Q)(Q quantity)
68         if (isQuantity!Q && is(Q.valueType == N))
69     {
70         mixin("import " ~ module_ ~ ";");
71         static assert(__traits(compiles, pow(quantity.rawValue, 1.0 / n)),
72                       "No overload of pow for an argument of type " ~ Q.valueType.stringof);
73         return Quantity!(Q.valueType, PowInverse!(n, Q.dimensions)).make(pow(quantity.rawValue, 1.0 / n));
74     }
75 
76     /// ditto
77     Q abs(Q)(Q quantity)
78         if (isQuantity!Q && is(Q.valueType == N))
79     {
80         mixin("import " ~ module_ ~ ";");
81         static assert(__traits(compiles, fabs(quantity.rawValue)),
82                       "No overload of fabs for an argument of type " ~ Q.valueType.stringof);
83         return Q.make(fabs(quantity.rawValue));
84     }
85 }
86 
87 ///
88 unittest
89 {
90     enum meter = unit!("L");
91     enum liter = 0.001 * meter * meter * meter;
92 
93     mixin MathFunctions!(real, "std.math");
94 
95     auto surface = 25 * square(meter);
96     auto side = sqrt(surface);
97     assert(side.value(meter).approxEqual(5));
98 
99     auto volume = 27 * liter;
100     side = cbrt(volume);
101     assert(side.value(meter).approxEqual(0.3));
102 
103     auto delta = -10 * meter;
104     assert(abs(delta) == 10 * meter);
105 }
106 
107 
108 /// Utility templates to manipulate quantity types.
109 template Inverse(Q)
110     if (isQuantity!Q)
111 {
112     alias Inverse = typeof(1 / Q.init);
113 }
114 
115 /// ditto
116 template Product(Q1, Q2)
117     if (isQuantity!Q1 && isQuantity!Q2)
118 {
119     alias Product = typeof(Q1.init * Q2.init);
120 }
121 
122 /// ditto
123 template Quotient(Q1, Q2)
124     if (isQuantity!Q1 && isQuantity!Q2)
125 {
126     alias Quotient = typeof(Q1.init / Q2.init);
127 }
128 
129 /// ditto
130 template Square(Q)
131     if (isQuantity!Q)
132 {
133     alias Square = typeof(Q.init * Q.init);
134 }
135 
136 /// ditto
137 template Cubic(Q, N = real)
138     if (isQuantity!Q)
139 {
140     alias Cubic = typeof(Q.init * Q.init * Q.init);
141 }
142 
143 ///
144 unittest
145 {
146     import quantities.si;
147 
148     static assert(is(Inverse!Time == Frequency));
149     static assert(is(Product!(Power, Time) == Energy));
150     static assert(is(Quotient!(Length, Time) == Speed));
151     static assert(is(Square!Length == Area));
152     static assert(is(Cubic!Length == Volume));
153     static assert(AreConsistent!(Product!(Inverse!Time, Length), Speed));
154 }