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 "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 {
23 public:
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 
161  const CUnitDimension operator+(const CUnitDimension &rhs) const
162  {
163  return CUnitDimension(*this) += rhs;
164  }
165 
166  const CUnitDimension operator-(const CUnitDimension &rhs) const
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 
334 private:
335  ExponentList m_EList;
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.
344 inline 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.
351 inline size_t hash_value(const CUnitDimension &ref)
352 {
353  return ref.hash_value();
354 }
355 
356 namespace 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 }
bool operator!=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:107
CUnitDimension & operator*=(const double &rhs)
Definition: UnitDimension.h:174
size_t hash_value() const
Definition: UnitDimension.h:322
const CUnitDimension operator*(const double &rhs) const
Definition: UnitDimension.h:184
const CUnitDimension operator-(const CUnitDimension &rhs) const
Definition: UnitDimension.h:166
bool operator<=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:310
void InitElems()
Definition: UnitDimension.cpp:20
STL namespace.
CCompoundUnitElement::ExponentType ExponentType
Definition: UnitDimension.h:24
size_t size() const
Definition: UnitDimension.h:37
CCompoundUnitElement::ExponentType const & operator[](size_t pos) const
Definition: UnitDimension.h:63
CUnitDimension & operator=(const CUnitDimension &rhs)
Definition: UnitDimension.h:48
bool IsFundamentalQuantity(size_t fundIdx) const
Definition: UnitDimension.h:192
bool operator>(const CUnitDimension &rhs) const
Definition: UnitDimension.h:305
bool operator>=(const CUnitDimension &rhs) const
Definition: UnitDimension.h:315
ExponentList m_EList
Definition: UnitDimension.h:335
CUnitDimension(const CUnitDimension &src)
Definition: UnitDimension.h:31
CUnitDimension()
Definition: UnitDimension.cpp:11
std::vector< ExponentType > ExponentList
Definition: UnitDimension.h:25
Definition: SnapValue.h:37
CUnitDimension & operator-=(const CUnitDimension &rhs)
Definition: UnitDimension.h:138
CCompoundUnitElement::ExponentType & operator[](size_t pos)
Definition: UnitDimension.h:56
size_t operator()(const CUnitDimension &ref) const
Definition: UnitDimension.h:361
Definition: UnitDimension.h:21
bool operator<(const CUnitDimension &rhs) const
Definition: UnitDimension.h:236
CUnitDimension & operator+=(const CUnitDimension &rhs)
Definition: UnitDimension.h:115
const CUnitDimension operator+(const CUnitDimension &rhs) const
Definition: UnitDimension.h:161
bool IsDimensionless() const
Definition: UnitDimension.h:216
bool operator==(const CUnitDimension &rhs) const
Definition: UnitDimension.h:74