UnitDimension.h
1/* Distributed under the Apache License, Version 2.0.
2 See accompanying NOTICE file for details.*/
3
4//----------------------------------------------------------------------------
10//----------------------------------------------------------------------------
11#pragma once
12
13#include "cdm/utils/unitconversion/CompoundUnitElement.h"
14
15// The nth element in our array contains the total exponent of the nth fundamental
16// quantity type in the transitive closure of the expansion of each unit in
17// a compound unit structure. The purpose of this object is to determine whether
18// two CompoundUnits are dimensionally compatible (i.e. whether one can be converted
19// to the other and vice-versa).
20
22{
23public:
25 typedef std::vector<ExponentType> ExponentList;
26
27 // Default ctor: size the array according to the number of fundamental quantities
29
30 // Copy ctor.
32 :m_EList(src.m_EList)
33 {
34
35 };
36
37 size_t size() const
38 {
39 return m_EList.size();
40 }
41
42 // Vectors of ints are probably initialized to zero anyway upon creation,
43 // but I'd rather not rely on that.
44 void InitElems();
45
46 // Operators that allow CCompoundUnit to construct our elements
47 // Always a good idea to overload operator= if providing a copy constructor
49 {
50 m_EList = rhs.m_EList;
51 return *this;
52 };
53
54 // Provide interface to indexing to the underlying vector elements. Alternative
55 // is exposing the vector pointer, which would be somewhat ugly.
57 {
58 return m_EList[pos];
59 };
60
61 // Not sure if we'd ever need to index into a const CCompoundUnitElement, but
62 // completeness is always a good thing.
64 {
65 return m_EList[pos];
66 };
67
68 // Test two CUnitDimension objects for equality. Here's where we want behavior
69 // that goes above and beyond what a vector does all by itself. It's conceivable,
70 // though unlikely, that application code may add new fundamental quantities
71 // after CUnitDimension objects have already been created. Thus, two CUnitDimension
72 // objects can be "equal" even if they're not the same size, so long as the longer
73 // one has zeros in its elements that are beyond the length of the shorter one.
74 bool operator==(const CUnitDimension &rhs) const
75 {
76 if (m_EList.size() == rhs.m_EList.size())
77 {
78 return (m_EList == rhs.m_EList);
79 }
80 else
81 {
82 size_t shorter = MIN(m_EList.size(), rhs.m_EList.size());
83 size_t longer = MAX(m_EList.size(), rhs.m_EList.size());
84 // First compare over elements they have in common
85 for (size_t i=0; i<shorter; ++i)
86 {
87 if (m_EList[i] != rhs.m_EList[i])
88 {
89 return false;
90 }
91 }
92 // Now figure out which vector is the longer one and check its extra
93 // elements to make sure they're all zero
94 const ExponentList &longerList = (m_EList.size()==longer ? m_EList : rhs.m_EList);
95 for (size_t i=shorter; i<longer; ++i)
96 {
97 if (longerList[i] != 0.0)
98 {
99 return false;
100 }
101 }
102 return true;
103 }
104 };
105
106 // Implement operator!= for sake of completeness
107 bool operator!=(const CUnitDimension &rhs) const
108 {
109 return ! (*this == rhs);
110 }
111
112 // Adding and subtracting dimension objects allows us to take shortcuts
113 // when multiplying and dividing CCompoundUnit objects whose CUnitDimension
114 // objects have already been built
116 {
117 size_t shorter = MIN(m_EList.size(), rhs.m_EList.size());
118 size_t longer = MAX(m_EList.size(), rhs.m_EList.size());
119 // First combine over elements they have in common
120 for (size_t i=0; i<shorter; ++i)
121 {
122 m_EList[i] += rhs.m_EList[i];
123 }
124
125 // If the right hand side is longer than *this, then resize *this and copy
126 // the extra elements
127 if ((longer>shorter) && (rhs.m_EList.size()==longer))
128 {
129 m_EList.resize(longer);
130 for (size_t i=shorter; i<longer; ++i)
131 {
132 m_EList[i] = rhs.m_EList[i];
133 }
134 } // if we need to copy more
135 return *this;
136 }
137
139 {
140 size_t shorter = MIN(m_EList.size(), rhs.m_EList.size());
141 size_t longer = MAX(m_EList.size(), rhs.m_EList.size());
142 // First combine over elements they have in common
143 for (size_t i=0; i<shorter; ++i)
144 {
145 m_EList[i] -= rhs.m_EList[i];
146 }
147
148 // If the right hand side is longer than *this, then resize *this and copy
149 // the extra elements
150 if ((longer>shorter) && (rhs.m_EList.size()==longer))
151 {
152 m_EList.resize(longer);
153 for (size_t i=shorter; i<longer; ++i)
154 {
155 m_EList[i] = -rhs.m_EList[i];
156 }
157 } // if we need to copy more
158 return *this;
159 }
160
162 {
163 return CUnitDimension(*this) += rhs;
164 }
165
167 {
168 return CUnitDimension(*this) -= rhs;
169 }
170
171 // Multiplying CUnitDimension by a scalar allows us to take shortcuts when
172 // raising to a power a CCompoundUnit object whose CUnitDimension object
173 // has already been computed
174 CUnitDimension & operator*= (const double &rhs)
175 {
176 size_t len = m_EList.size();
177 for (size_t i=0; i<len; ++i)
178 {
179 m_EList[i] *= rhs;
180 }
181 return *this;
182 }
183
184 const CUnitDimension operator* (const double &rhs) const
185 {
186 return CUnitDimension(*this) *= rhs;
187 }
188
189
190 // Determine if this UnitDimension object corresponds to a single
191 // specified fundamental quantity
192 bool IsFundamentalQuantity(size_t fundIdx) const
193 {
194 size_t len = m_EList.size();
195
196 // Rule out invalid params
197 if (fundIdx >= len)
198 {
199 return false;
200 }
201
202 int requiredVal;
203 for (size_t i=0; i<len; ++i)
204 {
205 //Must be 1 at the fundamental quantity index location, and zero everywhere else
206 requiredVal = ((i == fundIdx) ? 1 : 0);
207 if (m_EList[i] != requiredVal)
208 {
209 return false;
210 }
211 }
212 return true;
213 }
214
215 // Determine if this UnitDimension object is dimensionless
216 bool IsDimensionless() const
217 {
218 size_t len = m_EList.size();
219 for (size_t i=0; i<len; ++i)
220 {
221 if (m_EList[i] != 0)
222 {
223 return false;
224 }
225 }
226 return true;
227 }
228
229 // Since we use CUnitDimension objects as the basis for a hashmap key in
230 // CQuantityConversionKey, we need to implement a couple of methods for
231 // use by the CQuantityConversionKey object to help it implement the
232 // methods it needs in order for the default hash_map templated hash_compare
233 // object to work properly. Specifically, we need "operator<" to be defined
234 // properly, and we need a hash_value function.
235
236 bool operator<(const CUnitDimension &rhs) const
237 {
238 size_t shorter = MIN(m_EList.size(), rhs.m_EList.size());
239 size_t longer = MAX(m_EList.size(), rhs.m_EList.size());
240 // First compare over elements they have in common
241 for (size_t i=0; i<shorter; ++i)
242 {
243 if (m_EList[i] > rhs.m_EList[i])
244 {
245 return false;
246 }
247 else if (m_EList[i] < rhs.m_EList[i])
248 {
249 return true;
250 }
251 }
252
253 // If we got through the above loop without exiting, then all the
254 // elements in common are equal. If the vector lengths are the same,
255 // then *all* the elements are equal, and the less-than relationship
256 // is false
257 if (shorter == longer)
258 {
259 return false;
260 }
261
262 // The shorter vector is considered to be padded with zeros.
263 // Thus, compare the longer vector's elements to the imaginary
264 // zeros on the shorter
265 if (m_EList.size() > rhs.m_EList.size())
266 {
267 // ok, the left side is longer. Compare the excess left-side elements
268 // to the imaginary padded zeros
269 for (size_t i=shorter; i<longer; ++i)
270 {
271 if (m_EList[i] > 0.0)
272 {
273 return false;
274 }
275 else if (m_EList[i] < 0.0)
276 {
277 return true;
278 }
279 }
280 } // left side is longer
281 else
282 {
283 // ok, the right side is longer. Compare the excess right-side elements
284 // to the imaginary padded zeros
285 for (size_t i=shorter; i<longer; ++i)
286 {
287 if (rhs.m_EList[i] > 0.0)
288 {
289 return true;
290 }
291 else if (rhs.m_EList[i] < 0.0)
292 {
293 return false;
294 }
295 }
296 } // right side is longer
297
298 // If we get to this point, then the excess elements are all zeros,
299 // which means that the two CUnitDimension objects are equal, which
300 // means the less-than relationship is false.
301 return false;
302 }
303
304 // For the sake of completeness only, implement the other relational operators
305 bool operator>(const CUnitDimension &rhs) const
306 {
307 return rhs < *this;
308 }
309
310 bool operator <=(const CUnitDimension &rhs) const
311 {
312 return !(*this > rhs);
313 }
314
315 bool operator >=(const CUnitDimension &rhs) const
316 {
317 return !(*this < rhs);
318 }
319
320 // This hash value must be invariant with respect to trailing zeros
321 // at the end of the vector. See comment above operator==.
322 size_t hash_value() const
323 {
324 ExponentList::const_iterator iend = m_EList.end();
325 ExponentList::const_iterator ibgn = m_EList.begin();
326 // Find first trailing zero as new "end"
327 while ((iend != ibgn) && *--iend == 0);
328 iend++;
329
330 // Now invoke the collection hash routine provided in <xhash>
331 return _Hash_value(ibgn, iend);
332 }
333
334private:
336
337};
338
339// The member function operators work when the right operand is a double, but not
340// when the left operand is a double. So write a non-member operator* that
341// computes the result by invoking the member operator* with operands in the
342// reverse order. This is fine because multiplication of a CUnitDimension and
343// a scalar double val is an abelian operation.
344inline const CUnitDimension operator* (const double &lhs, const CUnitDimension &rhs)
345{
346 return rhs * lhs;
347}
348
349// Overload non_member hash_value on CUnitDimension so that the
350// templated hash_compare used by hash_map can call it.
351inline size_t hash_value(const CUnitDimension &ref)
352{
353 return ref.hash_value();
354}
355
356namespace std
357{
358 template<>
359 struct hash<CUnitDimension>
360 {
361 size_t operator()(const CUnitDimension& ref) const
362 {
363 return ref.hash_value();
364 }
365 };
366}
Definition: SnapValue.h:38
Definition: UnitDimension.h:22
CUnitDimension & operator*=(const double &rhs)
Definition: UnitDimension.h:174
CCompoundUnitElement::ExponentType & operator[](size_t pos)
Definition: UnitDimension.h:56
ExponentList m_EList
Definition: UnitDimension.h:335
bool operator<(const CUnitDimension &rhs) const
Definition: UnitDimension.h:236
bool operator!=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:107
size_t size() const
Definition: UnitDimension.h:37
std::vector< ExponentType > ExponentList
Definition: UnitDimension.h:25
const CUnitDimension operator+(const CUnitDimension &rhs) const
Definition: UnitDimension.h:161
CUnitDimension(const CUnitDimension &src)
Definition: UnitDimension.h:31
const CUnitDimension operator-(const CUnitDimension &rhs) const
Definition: UnitDimension.h:166
CCompoundUnitElement::ExponentType ExponentType
Definition: UnitDimension.h:24
bool IsDimensionless() const
Definition: UnitDimension.h:216
CCompoundUnitElement::ExponentType const & operator[](size_t pos) const
Definition: UnitDimension.h:63
CUnitDimension & operator+=(const CUnitDimension &rhs)
Definition: UnitDimension.h:115
bool operator>=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:315
CUnitDimension()
Definition: UnitDimension.cpp:11
const CUnitDimension operator*(const double &rhs) const
Definition: UnitDimension.h:184
void InitElems()
Definition: UnitDimension.cpp:20
CUnitDimension & operator-=(const CUnitDimension &rhs)
Definition: UnitDimension.h:138
bool IsFundamentalQuantity(size_t fundIdx) const
Definition: UnitDimension.h:192
size_t hash_value() const
Definition: UnitDimension.h:322
bool operator<=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:310
bool operator>(const CUnitDimension &rhs) const
Definition: UnitDimension.h:305
bool operator==(const CUnitDimension &rhs) const
Definition: UnitDimension.h:74
CUnitDimension & operator=(const CUnitDimension &rhs)
Definition: UnitDimension.h:48
STL namespace.
size_t operator()(const CUnitDimension &ref) const
Definition: UnitDimension.h:361

Distributed under the Apache License, Version 2.0.

See accompanying NOTICE file for details.