1 // quantity.cpp
2 // Support for units and dimensions
3 //
4 // This file is part of the SpeedCrunch project
5 // Copyright (C) 2016 Pol Welter.
6 // Copyright (C) 2016 @heldercorreia
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; see the file COPYING.  If not, write to
20 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 // Boston, MA 02110-1301, USA.
22 
23 
24 #include "quantity.h"
25 
26 #include "rational.h"
27 #include "units.h"
28 
29 #include <QStringList>
30 
31 #define RATIONAL_TOL HNumber("1e-20")
32 
33 #define ENSURE_DIMENSIONLESS(x) \
34     if (!(x).isDimensionless()) \
35         return DMath::nan(InvalidDimension);
36 
37 #define ENSURE_SAME_DIMENSION(x, y) \
38     if ((!(x).sameDimension(y))) \
39         return DMath::nan(DimensionMismatch);
40 
operator -(const Quantity & q)41 Quantity operator-(const Quantity& q)
42 {
43     Quantity res(q);
44     res.m_numericValue = -res.m_numericValue;
45     return res;
46 }
47 
operator -(const Quantity & a,const Quantity & b)48 Quantity operator-(const Quantity& a, const Quantity& b)
49 {
50     Quantity res(a);
51     if (!a.sameDimension(b))
52         return DMath::nan(DimensionMismatch);
53     res.m_numericValue -= b.m_numericValue;
54     return res;
55 }
56 
operator >(const Quantity & l,const Quantity & r)57 bool operator>(const Quantity& l, const Quantity& r)
58 {
59     if (l.sameDimension(r))
60         return l.m_numericValue > r.m_numericValue;
61     return false;
62 }
63 
operator <(const Quantity & l,const Quantity & r)64 bool operator<(const Quantity& l, const Quantity& r)
65 {
66     if (l.sameDimension(r))
67         return l.m_numericValue < r.m_numericValue;
68     return false;
69 }
70 
operator >=(const Quantity & l,const Quantity & r)71 bool operator>=(const Quantity& l, const Quantity& r)
72 {
73     if (l.sameDimension(r))
74         return l.m_numericValue >= r.m_numericValue;
75     return false;
76 }
77 
operator <=(const Quantity & l,const Quantity & r)78 bool operator<=(const Quantity& l, const Quantity& r)
79 {
80     if (l.sameDimension(r))
81         return l.m_numericValue <= r.m_numericValue;
82     return false;
83 }
84 
operator ==(const Quantity & l,const Quantity & r)85 bool operator==(const Quantity& l, const Quantity& r)
86 {
87     if (l.sameDimension(r))
88         return l.m_numericValue == r.m_numericValue;
89     return false;
90 }
91 
92 // Returns TRUE upon dimension mismatch.
operator !=(const Quantity & l,const Quantity & r)93 bool operator!=(const Quantity& l, const Quantity& r)
94 {
95     if (l.sameDimension(r))
96         return l.m_numericValue != r.m_numericValue;
97     return true;
98 }
99 
operator *(const HNumber & l,const Quantity & r)100 Quantity operator*(const HNumber& l, const Quantity& r)
101 {
102     return r * l;
103 }
104 
operator *(const CNumber & l,const Quantity & r)105 Quantity operator*(const CNumber& l, const Quantity& r)
106 {
107     return r * l;
108 }
109 
operator /(const HNumber & l,const Quantity & r)110 Quantity operator/(const HNumber& l, const Quantity& r)
111 {
112     return Quantity(l) / r;
113 }
114 
operator /(const CNumber & l,const Quantity & r)115 Quantity operator/(const CNumber& l, const Quantity& r)
116 {
117     return Quantity(l) / r;
118 }
119 
Quantity()120 Quantity::Quantity()
121     : m_numericValue(0)
122     , m_unit(nullptr)
123     , m_unitName("")
124 {
125 }
126 
Quantity(const Quantity & other)127 Quantity::Quantity(const Quantity& other)
128     : m_numericValue(other.m_numericValue)
129     , m_dimension(other.m_dimension)
130     , m_unit(nullptr)
131     , m_unitName(other.m_unitName)
132     , m_format(other.m_format)
133 {
134     if (other.hasUnit())
135         this->m_unit = new CNumber(other.unit());
136     cleanDimension();
137 }
138 
Quantity(int i)139 Quantity::Quantity(int i)
140     : Quantity(CNumber(i))
141 {
142 }
143 
Quantity(const QJsonObject & json)144 Quantity::Quantity(const QJsonObject& json)
145     : Quantity()
146 {
147     *this = deSerialize(json);
148 }
149 
Quantity(const HNumber & h)150 Quantity::Quantity(const HNumber& h)
151     : Quantity(CNumber(h))
152 {
153 }
154 
Quantity(const CNumber & c)155 Quantity::Quantity(const CNumber& c)
156     : Quantity()
157 {
158     this->m_numericValue = c;
159 }
160 
~Quantity()161 Quantity::~Quantity()
162 {
163     delete m_unit;
164 }
165 
isNan() const166 bool Quantity::isNan() const
167 {
168     return m_numericValue.isNan();
169 }
170 
isZero() const171 bool Quantity::isZero() const
172 {
173     return m_numericValue.isZero();
174 }
175 
isReal() const176 bool Quantity::isReal() const
177 {
178     return m_numericValue.isReal();
179 }
180 
isPositive() const181 bool Quantity::isPositive() const
182 {
183     return m_numericValue.isPositive();
184 }
185 
isNegative() const186 bool Quantity::isNegative() const
187 {
188     return m_numericValue.isNegative();
189 }
190 
isInteger() const191 bool Quantity::isInteger() const
192 {
193     return (!this->hasDimension() && !this->hasUnit()) && m_numericValue.isInteger();
194 }
195 
hasUnit() const196 bool Quantity::hasUnit() const
197 {
198     return this->m_unit != NULL;
199 }
200 
unit() const201 CNumber Quantity::unit() const
202 {
203     if (this->hasUnit())
204         return CNumber(*(this->m_unit));
205     return CNumber(1);
206 }
207 
unitName() const208 QString Quantity::unitName() const
209 {
210     if (this->hasUnit())
211         return m_unitName;
212     return "";
213 }
214 
numericValue() const215 CNumber Quantity::numericValue() const
216 {
217     return m_numericValue;
218 }
219 
setDisplayUnit(const CNumber unit,const QString & name)220 Quantity& Quantity::setDisplayUnit(const CNumber unit, const QString& name)
221 {
222     if (unit.isNan())
223         *this = DMath::nan(InvalidDimension);
224     else {
225         stripUnits();
226         m_unit = new CNumber(unit);
227         m_unitName = name;
228     }
229     return *this;
230 }
231 
setFormat(Format c)232 Quantity& Quantity::setFormat(Format c)
233 {
234     m_format = c;
235     return *this;
236 }
237 
stripUnits()238 void Quantity::stripUnits()
239 {
240     delete m_unit;
241     m_unit = nullptr;
242     m_unitName = "";
243 }
244 
hasDimension() const245 bool Quantity::hasDimension() const
246 {
247     return !this->m_dimension.empty();
248 }
249 
250 /*
251  * Unlike hasDimension(), this does a clean up first, i.e. it
252  * checks for redundant exponents.
253  */
isDimensionless() const254 bool Quantity::isDimensionless() const
255 {
256     Quantity temp(*this);
257     temp.cleanDimension();
258     return m_dimension.empty();
259 }
260 
getDimension() const261 QMap<QString, Rational> Quantity::getDimension() const
262 {
263     Quantity temp(*this);
264     temp.cleanDimension();
265     return temp.m_dimension;
266 }
267 
modifyDimension(const QString & key,const Rational & exponent)268 void Quantity::modifyDimension(const QString& key, const Rational& exponent)
269 {
270     if (exponent.isZero())
271         m_dimension.remove(key);
272     else
273         m_dimension.insert(key, exponent);
274 }
275 
copyDimension(const Quantity & other)276 void Quantity::copyDimension(const Quantity& other)
277 {
278     clearDimension();
279     this->m_dimension = other.m_dimension;
280 }
281 
clearDimension()282 void Quantity::clearDimension()
283 {
284     this->m_dimension.clear();
285 }
286 
287 // Note: Does NOT clean the dimension vector first.
288 // The calling function must do so on its own.
sameDimension(const Quantity & other) const289 bool Quantity::sameDimension(const Quantity& other) const
290 {
291     return this->m_dimension == other.m_dimension;
292 }
293 
cleanDimension()294 void Quantity::cleanDimension()
295 {
296     auto i = m_dimension.begin();
297     while (i != m_dimension.end()) {
298         if (i.value().isZero())
299             i = m_dimension.erase(i);
300         else
301             ++i;
302     }
303 }
304 
serialize(QJsonObject & json) const305 void Quantity::serialize(QJsonObject& json) const
306 {
307     QJsonObject nom_json;
308     m_numericValue.serialize(nom_json);
309     json["numeric_value"] = nom_json;
310 
311     if (hasDimension()) {
312         QJsonObject dim_json;
313         auto i = m_dimension.constBegin();
314         while (i != m_dimension.constEnd()) {
315             const auto& exp = i.value();
316             const auto& name = i.key();
317             dim_json[name] = exp.toString();
318             ++i;
319         }
320         json["dimension"] = dim_json;
321     }
322 
323     if (hasUnit()) {
324         QJsonObject unit_json;
325         m_unit->serialize(unit_json);
326         json["unit"] = unit_json;
327         json["unit_name"] = m_unitName;
328     }
329 
330     if (!m_format.isNull()) {
331         QJsonObject format_json;
332         m_format.serialize(format_json);
333         json["format"] = format_json;
334     }
335 }
336 
deSerialize(const QJsonObject & json)337 Quantity Quantity::deSerialize(const QJsonObject& json)
338 {
339     Quantity result;
340     if (json.contains("numeric_value")) {
341         QJsonObject num_json = json["numeric_value"].toObject();
342         result.m_numericValue = CNumber(num_json);
343     }
344     result.stripUnits();
345     if (json.contains("unit")) {
346         QJsonObject unit_json = json["unit"].toObject();
347         result.m_unit = new CNumber(unit_json);
348     }
349     if (json.contains("unit_name"))
350         result.m_unitName = json["unit_name"].toString();
351 
352     if (json.contains("dimension")) {
353         QJsonObject dim_json = json["dimension"].toObject();
354         for (int i = 0; i < dim_json.count(); ++i) {
355             auto key = dim_json.keys().at(i);
356             Rational val(dim_json[key].toString());
357             result.modifyDimension(key, val);
358         }
359     }
360     if (json.contains("format")) {
361         QJsonObject format_json = json["format"].toObject();
362         result.m_format = Quantity::Format::deSerialize(format_json);
363     }
364     return result;
365 }
366 
error() const367 Error Quantity::error() const
368 {
369     return m_numericValue.error();
370 }
371 
operator =(const Quantity & other)372 Quantity& Quantity::operator=(const Quantity& other)
373 {
374     m_numericValue = other.m_numericValue;
375     m_dimension = other.m_dimension;
376     m_format = other.m_format;
377     stripUnits();
378     if(other.hasUnit()) {
379         m_unit = new CNumber(*other.m_unit);
380         m_unitName = other.m_unitName;
381     }
382     cleanDimension();
383     return *this;
384 }
385 
operator +(const Quantity & other) const386 Quantity Quantity::operator+(const Quantity& other) const
387 {
388     if (!this->sameDimension(other))
389         return DMath::nan(DimensionMismatch);
390     Quantity result(*this);
391     result.m_numericValue += other.m_numericValue;
392     return result;
393 }
394 
operator +=(const Quantity & other)395 Quantity& Quantity::operator+=(const Quantity& other)
396 {
397     if (!this->sameDimension(other))
398         *this = DMath::nan(DimensionMismatch);
399     else
400         this->m_numericValue += other.m_numericValue;
401     return *this;
402 }
403 
operator -=(const Quantity & other)404 Quantity& Quantity::operator-=(const Quantity& other)
405 {
406     return operator=(*this - other);
407 }
408 
operator *(const Quantity & other) const409 Quantity Quantity::operator*(const Quantity& other) const
410 {
411     Quantity result(*this);
412     result.m_numericValue *= other.m_numericValue;
413     if (!other.isDimensionless()) {
414         result.stripUnits();
415         auto i = other.m_dimension.constBegin();
416         while (i != other.m_dimension.constEnd()) {
417             const auto& exp = i.value();
418             const auto& name = i.key();
419             if (!result.m_dimension.contains(name))
420                 result.m_dimension[name] = Rational(0);
421             result.m_dimension[name] += exp;
422             ++i;
423         }
424         result.cleanDimension();
425     }
426     return result;
427 }
428 
operator *(const CNumber & other) const429 Quantity Quantity::operator*(const CNumber& other) const
430 {
431     Quantity result(*this);
432     result.m_numericValue *= other;
433     return result;
434 }
435 
operator *(const HNumber & other) const436 Quantity Quantity::operator*(const HNumber& other) const
437 {
438     return operator*(CNumber(other));
439 }
440 
operator *=(const Quantity & other)441 Quantity &Quantity::operator*=(const Quantity& other)
442 {
443     return operator=(*this * other);
444 }
445 
operator /(const Quantity & other) const446 Quantity Quantity::operator/(const Quantity& other) const
447 {
448     Quantity result(*this);
449     result.m_numericValue /= other.m_numericValue;
450     if (!other.isDimensionless()) {
451         result.stripUnits();
452         auto i = other.m_dimension.constBegin();
453         while (i != other.m_dimension.constEnd()) {
454             const auto& exp = i.value();
455             const auto& name = i.key();
456             if (!result.m_dimension.contains(name))
457                 result.m_dimension[name] = Rational(0);
458             result.m_dimension[name] -= exp;
459             ++i;
460         }
461         result.cleanDimension();
462     }
463     return result;
464 }
465 
operator /(const HNumber & other) const466 Quantity Quantity::operator/(const HNumber& other) const
467 {
468     return operator/(CNumber(other));
469 }
470 
operator /(const CNumber & other) const471 Quantity Quantity::operator/(const CNumber& other) const
472 {
473     Quantity result(*this);
474     result.m_numericValue /= other;
475     result.cleanDimension();
476     return result;
477 }
478 
operator /=(const Quantity & other)479 Quantity &Quantity::operator/=(const Quantity& other)
480 {
481     return operator=(*this/other);
482 }
483 
operator %(const Quantity & other) const484 Quantity Quantity::operator%(const Quantity& other) const
485 {
486     Quantity result(*this);
487     result.m_numericValue = result.m_numericValue % other.m_numericValue;
488     return result;
489 }
490 
operator &(const Quantity & other) const491 Quantity Quantity::operator&(const Quantity& other) const
492 {
493     ENSURE_DIMENSIONLESS(*this);
494     ENSURE_DIMENSIONLESS(other);
495     Quantity result(*this);
496     result.m_numericValue &= other.m_numericValue;
497     return result;
498 }
499 
operator &=(const Quantity & other)500 Quantity &Quantity::operator&=(const Quantity& other)
501 {
502     return operator=(*this & other);
503 }
504 
operator |(const Quantity & other) const505 Quantity Quantity::operator|(const Quantity& other) const
506 {
507     ENSURE_DIMENSIONLESS(*this);
508     ENSURE_DIMENSIONLESS(other);
509     Quantity result(*this);
510     result.m_numericValue |= other.m_numericValue;
511     return result;
512 }
513 
operator |=(const Quantity & other)514 Quantity &Quantity::operator|=(const Quantity& other)
515 {
516     return operator=(*this | other);
517 }
518 
operator ^(const Quantity & other) const519 Quantity Quantity::operator^(const Quantity& other) const
520 {
521     ENSURE_DIMENSIONLESS(*this);
522     ENSURE_DIMENSIONLESS(other);
523     Quantity result(*this);
524     result.m_numericValue ^= other.m_numericValue;
525     return result;
526 }
527 
operator ^=(const Quantity & other)528 Quantity &Quantity::operator^=(const Quantity& other)
529 {
530     return operator=(*this ^ other);
531 }
532 
operator ~() const533 Quantity Quantity::operator~() const
534 {
535     ENSURE_DIMENSIONLESS(*this);
536     Quantity result(*this);
537     result.m_numericValue= ~result.m_numericValue;
538     return result;
539 }
540 
operator >>(const Quantity & other) const541 Quantity Quantity::operator>>(const Quantity& other) const
542 {
543     ENSURE_DIMENSIONLESS(*this);
544     ENSURE_DIMENSIONLESS(other);
545     Quantity result(*this);
546     result.m_numericValue = result.m_numericValue >> other.m_numericValue;
547     return result;
548 }
549 
operator <<(const Quantity & other) const550 Quantity Quantity::operator<<(const Quantity& other) const
551 {
552     ENSURE_DIMENSIONLESS(*this);
553     ENSURE_DIMENSIONLESS(other);
554     Quantity result(*this);
555     result.m_numericValue = result.m_numericValue << other.m_numericValue;
556     return result;
557 }
558 
Format()559 Quantity::Format::Format()
560     : CNumber::Format()
561 {
562 }
563 
Format(const CNumber::Format & other)564 Quantity::Format::Format(const CNumber::Format& other)
565     : CNumber::Format(other)
566 {
567 }
568 
Format(const HNumber::Format & other)569 Quantity::Format::Format(const HNumber::Format& other)
570     : CNumber::Format(other)
571 {
572 }
573 
operator +(const Quantity::Format & other) const574 Quantity::Format Quantity::Format::operator+(const Quantity::Format& other) const
575 {
576     return Quantity::Format(CNumber::Format::operator+(static_cast<const CNumber::Format&>(other)));
577 }
578 
serialize(QJsonObject & json) const579 void Quantity::Format::serialize(QJsonObject& json) const
580 {
581     switch (mode) {
582     case Mode::General:
583         json["mode"] = QStringLiteral("General");
584         break;
585     case Mode::Fixed:
586         json["mode"] = QStringLiteral("Fixed");
587         break;
588     case Mode::Scientific:
589         json["mode"] = QStringLiteral("Scientific");
590         break;
591     case Mode::Engineering:
592         json["mode"] = QStringLiteral("Engineering");
593         break;
594     case Mode::Sexagesimal:
595         json["mode"] = QStringLiteral("Sexagesimal");
596         break;
597     case Mode::Null:
598         break;
599     }
600 
601     switch (base) {
602     case Base::Binary:
603         json["base"] = QStringLiteral("Binary");
604         break;
605     case Base::Octal:
606         json["base"] = QStringLiteral("Octal");
607         break;
608     case Base::Hexadecimal:
609         json["base"] = QStringLiteral("Hexadecimal");
610         break;
611     case Base::Decimal:
612         json["base"] = QStringLiteral("Decimal");
613         break;
614     case Base::Null:
615         break;
616     }
617 
618     switch (notation) {
619     case Notation::Cartesian:
620         json["form"] = QStringLiteral("Cartesian");
621         break;
622     case Notation::Polar:
623         json["form"] = QStringLiteral("Polar");
624         break;
625     case Notation::Null:
626     default:
627         break;
628     }
629 
630     if (precision != PrecisionNull)
631         json["precision"] = precision;
632 }
633 
deSerialize(const QJsonObject & json)634 Quantity::Format Quantity::Format::deSerialize(const QJsonObject& json)
635 {
636     Format result;
637     if (json.contains("mode")) {
638         auto strMode = json["mode"].toString();
639         if (strMode == "General")
640             result.mode = Mode::General;
641         else if (strMode == "Fixed")
642             result.mode = Mode::Fixed;
643         else if (strMode == "Scientific")
644             result.mode = Mode::Scientific;
645         else if (strMode == "Engineering")
646             result.mode = Mode::Engineering;
647         else if (strMode == "Sexagesimal")
648             result.mode = Mode::Sexagesimal;
649         else
650             result.mode = Mode::Null;
651     } else
652         result.mode = Mode::Null;
653 
654     if (json.contains("base")) {
655         auto strBase = json["base"].toString();
656         if (strBase == "Binary")
657             result.base = Base::Binary;
658         else if (strBase == "Octal")
659             result.base = Base::Octal;
660         else if (strBase == "Decimal")
661             result.base = Base::Decimal;
662         else if (strBase == "Hexadecimal")
663             result.base = Base::Hexadecimal;
664         else
665             result.base = Base::Null;
666     } else
667         result.base = Base::Null;
668 
669     if (json.contains("form")) {
670         auto strNotation = json["form"].toString();
671         if (strNotation == "Cartesian")
672             result.notation = Notation::Cartesian;
673         else if (strNotation == "Polar")
674             result.notation = Notation::Polar;
675         else
676             result.notation = Notation::Null;
677     } else
678         result.notation = Notation::Null;
679 
680 
681     result.precision = json.contains("precision") ? json["precision"].toInt() : PrecisionNull;
682     return result;
683 }
684 
isNull() const685 bool Quantity::Format::isNull() const
686 {
687     return (mode == Mode::Null && base == Base::Null && precision == PrecisionNull && notation == Notation::Null);
688 }
689 
690 // DMath
691 // =====
692 
693 bool DMath::complexMode = true;
694 
695 #define COMPLEX_WRAP_1(fct, arg) \
696     (DMath::complexMode ? CMath::fct(arg) : CNumber(HMath::fct(arg.real)))
697 
698 #define COMPLEX_WRAP_2(fct, arg1, arg2) \
699     (DMath::complexMode ? CMath::fct(arg1, arg2) : CNumber(HMath::fct(arg1.real, arg2.real)))
700 
701 #define COMPLEX_WRAP_3(fct, arg1, arg2, arg3) \
702     (DMath::complexMode ? CMath::fct(arg1, arg2, arg3) : CNumber(HMath::fct(arg1.real, arg2.real, arg3.real)))
703 
704 #define COMPLEX_WRAP_4(fct, arg1, arg2, arg3, arg4) \
705     (DMath::complexMode ? CMath::fct(arg1, arg2, arg3, arg4) \
706     : CNumber(HMath::fct(arg1.real, arg2.real, arg3.real, arg4.real)))
707 
708 //  Wrappers for functions that are only defined for dimensionless arguments
709 
710 // Mo argument.
711 #define WRAPPER_DMATH_0(fct) \
712     Quantity DMath::fct() \
713     { \
714         return Quantity(CMath::fct()); \
715     } \
716 
717 // One argument.
718 #define WRAPPER_DMATH_1(fct) \
719     Quantity DMath::fct(const Quantity& arg1) \
720     { \
721         ENSURE_DIMENSIONLESS(arg1); \
722         return Quantity(COMPLEX_WRAP_1(fct, arg1.m_numericValue)); \
723     }
724 
725 // Two arguments.
726 #define WRAPPER_DMATH_2(fct) \
727     Quantity DMath::fct(const Quantity& arg1, const Quantity& arg2) \
728     { \
729         ENSURE_DIMENSIONLESS(arg1); \
730         ENSURE_DIMENSIONLESS(arg2); \
731         return Quantity(COMPLEX_WRAP_2(fct, arg1.m_numericValue, arg2.m_numericValue)); \
732     }
733 
734 // Three arguments.
735 #define WRAPPER_DMATH_3(fct) \
736     Quantity DMath::fct(const Quantity& arg1, const Quantity& arg2, const Quantity& arg3) \
737     { \
738         ENSURE_DIMENSIONLESS(arg1); \
739         ENSURE_DIMENSIONLESS(arg2); \
740         ENSURE_DIMENSIONLESS(arg3); \
741         return Quantity(COMPLEX_WRAP_3(fct, arg1.m_numericValue, arg2.m_numericValue, arg3.m_numericValue)); \
742     }
743 
744 // Four arguments.
745 #define WRAPPER_DMATH_4(fct) \
746     Quantity DMath::fct(const Quantity& arg1, const Quantity& arg2, const Quantity& arg3, const Quantity& arg4) \
747     { \
748         ENSURE_DIMENSIONLESS(arg1); \
749         ENSURE_DIMENSIONLESS(arg2); \
750         ENSURE_DIMENSIONLESS(arg3); \
751         ENSURE_DIMENSIONLESS(arg4); \
752         return Quantity(COMPLEX_WRAP_4(fct, arg1.m_numericValue, arg2.m_numericValue, arg3.m_numericValue, \
753                                        arg4.m_numericValue)); \
754     }
755 
756 WRAPPER_DMATH_0(e)
WRAPPER_DMATH_0(pi)757 WRAPPER_DMATH_0(pi)
758 WRAPPER_DMATH_0(phi)
759 WRAPPER_DMATH_0(i)
760 
761 Quantity DMath::nan(Error error)
762 {
763     return Quantity(CMath::nan(error));
764 }
765 
766 WRAPPER_DMATH_1(rad2deg)
WRAPPER_DMATH_1(deg2rad)767 WRAPPER_DMATH_1(deg2rad)
768 WRAPPER_DMATH_1(rad2gon)
769 WRAPPER_DMATH_1(gon2rad)
770 WRAPPER_DMATH_1(integer)
771 WRAPPER_DMATH_1(frac)
772 WRAPPER_DMATH_1(floor)
773 WRAPPER_DMATH_1(ceil)
774 WRAPPER_DMATH_1(exp)
775 WRAPPER_DMATH_1(ln)
776 WRAPPER_DMATH_1(lg)
777 WRAPPER_DMATH_1(lb)
778 WRAPPER_DMATH_2(log)
779 WRAPPER_DMATH_1(sinh)
780 WRAPPER_DMATH_1(cosh)
781 WRAPPER_DMATH_1(tanh)
782 WRAPPER_DMATH_1(arsinh)
783 WRAPPER_DMATH_1(arcosh)
784 WRAPPER_DMATH_1(artanh)
785 WRAPPER_DMATH_1(sin)
786 WRAPPER_DMATH_1(cos)
787 WRAPPER_DMATH_1(tan)
788 WRAPPER_DMATH_1(cot)
789 WRAPPER_DMATH_1(sec)
790 WRAPPER_DMATH_1(csc)
791 WRAPPER_DMATH_1(arcsin)
792 WRAPPER_DMATH_1(arccos)
793 WRAPPER_DMATH_1(arctan)
794 WRAPPER_DMATH_2(arctan2)
795 
796 WRAPPER_DMATH_2(factorial)
797 WRAPPER_DMATH_1(gamma)
798 WRAPPER_DMATH_1(lnGamma)
799 WRAPPER_DMATH_1(erf)
800 WRAPPER_DMATH_1(erfc)
801 
802 WRAPPER_DMATH_2(gcd)
803 WRAPPER_DMATH_2(idiv)
804 
805 Quantity DMath::round(const Quantity& n, int prec)
806 {
807     ENSURE_DIMENSIONLESS(n);
808     return DMath::complexMode ?
809         CMath::round(n.numericValue(), prec) :
810         CNumber(HMath::round(n.numericValue().real, prec));
811 }
812 
trunc(const Quantity & n,int prec)813 Quantity DMath::trunc(const Quantity& n, int prec)
814 {
815     ENSURE_DIMENSIONLESS(n);
816     return DMath::complexMode ?
817         CMath::trunc(n.numericValue(), prec)
818         : CNumber(HMath::trunc(n.numericValue().real, prec));
819 }
820 
821 WRAPPER_DMATH_2(nCr)
WRAPPER_DMATH_2(nPr)822 WRAPPER_DMATH_2(nPr)
823 WRAPPER_DMATH_3(binomialPmf)
824 WRAPPER_DMATH_3(binomialCdf)
825 WRAPPER_DMATH_2(binomialMean)
826 WRAPPER_DMATH_2(binomialVariance)
827 WRAPPER_DMATH_4(hypergeometricPmf)
828 WRAPPER_DMATH_4(hypergeometricCdf)
829 WRAPPER_DMATH_3(hypergeometricMean)
830 WRAPPER_DMATH_3(hypergeometricVariance)
831 WRAPPER_DMATH_2(poissonPmf)
832 WRAPPER_DMATH_2(poissonCdf)
833 WRAPPER_DMATH_1(poissonMean)
834 WRAPPER_DMATH_1(poissonVariance)
835 
836 WRAPPER_DMATH_2(mask)
837 WRAPPER_DMATH_2(sgnext)
838 WRAPPER_DMATH_2(ashr)
839 
840 
841 WRAPPER_DMATH_3(decodeIeee754)
842 WRAPPER_DMATH_4(decodeIeee754)
843 
844 
845 QString DMath::format(Quantity q, Quantity::Format format)
846 {
847     format = q.format() + format;  // Left hand side operator takes priority.
848 
849     // Handle units.
850     if (!q.hasUnit() && !q.isDimensionless()) {
851         q.cleanDimension();
852         Units::findUnit(q);
853     }
854     QString unit_name = ' ' + q.unitName();
855     CNumber unit = q.unit();
856     CNumber number = q.m_numericValue;
857 
858     number /= unit;
859 
860     QString result = CMath::format(number, format);
861 
862     if (!number.real.isZero() && !number.imag.isZero() && unit_name != " ")
863         result = "(" + result + ")";
864 
865     if (unit_name != " ")
866         result.append(unit_name);
867 
868     return result;
869 }
870 
real(const Quantity & x)871 Quantity DMath::real(const Quantity& x)
872 {
873     Quantity result(x);
874     result.m_numericValue = result.m_numericValue.real;
875     return result;
876 }
877 
imag(const Quantity & x)878 Quantity DMath::imag(const Quantity& x)
879 {
880     Quantity result(x);
881     result.m_numericValue = result.m_numericValue.imag;
882     return result;
883 }
884 
conj(const Quantity & n)885 Quantity DMath::conj(const Quantity& n)
886 {
887     Quantity result = Quantity(n);
888     // If in Real mode, just strip the imaginary part.
889     result.m_numericValue = complexMode ?
890         CMath::conj(result.m_numericValue)
891         : CMath::real(result.m_numericValue);
892 
893     return result;
894 }
895 
abs(const Quantity & n)896 Quantity DMath::abs(const Quantity& n)
897 {
898     Quantity result(n);
899     result.m_numericValue = COMPLEX_WRAP_1(abs, n.m_numericValue);
900     return result;
901 }
902 
phase(const Quantity & n)903 Quantity DMath::phase(const Quantity& n)
904 {
905     return CMath::phase(n.numericValue());
906 }
907 
sqrt(const Quantity & n)908 Quantity DMath::sqrt(const Quantity& n)
909 {
910     Quantity result(COMPLEX_WRAP_1(sqrt, n.m_numericValue));
911     auto i = n.m_dimension.constBegin();
912     while (i != n.m_dimension.constEnd()) {
913         auto& exp = i.value();
914         auto& name = i.key();
915         result.modifyDimension(name, exp * Rational(1,2));
916         ++i;
917     }
918     return result;
919 }
920 
cbrt(const Quantity & n)921 Quantity DMath::cbrt(const Quantity& n)
922 {
923     Quantity result(COMPLEX_WRAP_1(cbrt, n.m_numericValue));
924     auto i = n.m_dimension.constBegin();
925     while (i != n.m_dimension.constEnd()) {
926         auto& exp = i.value();
927         auto& name = i.key();
928         result.modifyDimension(name, exp * Rational(1,3));
929         ++i;
930     }
931     return result;
932 }
933 
raise(const Quantity & n1,int n)934 Quantity DMath::raise(const Quantity& n1, int n)
935 {
936     Quantity result;
937     result.m_numericValue = complexMode ?
938         CMath::raise(n1.m_numericValue, n)
939         : CNumber(HMath::raise(n1.m_numericValue.real, n));
940     auto i = n1.m_dimension.constBegin();
941     while (i != n1.m_dimension.constEnd()) {
942         auto& exp = i.value();
943         auto& name = i.key();
944         result.modifyDimension(name, exp * n);
945         ++i;
946     }
947     return result;
948 }
949 
raise(const Quantity & n1,const Quantity & n2)950 Quantity DMath::raise(const Quantity& n1, const Quantity& n2)
951 {
952     if (!n2.isDimensionless() || (!n1.isDimensionless() && !n2.isReal() && complexMode))
953         return DMath::nan(InvalidDimension);
954 
955     // First get the new numeric value.
956     Quantity result(COMPLEX_WRAP_2(raise, n1.m_numericValue, n2.m_numericValue));
957 
958     if (n1.isDimensionless())
959         return result;
960 
961     // We can now assume that n1 has a dimension, but n2 is real.
962     // Compute the new dimension: try to convert n2 to a Rational. If n2 is not
963     // rational, return NaN.
964 
965     // For negative bases only allow odd denominators.
966     Rational exponent(n2.m_numericValue.real);
967     if (abs(exponent.toHNumber() - n2.m_numericValue.real) >= RATIONAL_TOL
968        || (n1.isNegative() && exponent.denominator() % 2 == 0))
969         return DMath::nan(OutOfDomain);
970 
971     // Compute new dimension.
972     auto i = n1.m_dimension.constBegin();
973     while (i != n1.m_dimension.constEnd()) {
974         result.modifyDimension(i.key(), i.value()*exponent);
975         ++i;
976     }
977     return result;
978 }
979 
sgn(const Quantity & x)980 Quantity DMath::sgn(const Quantity& x)
981 {
982     return Quantity(CMath::sgn(x.m_numericValue));
983 }
984 
encodeIeee754(const Quantity & val,const Quantity & exp_bits,const Quantity & significand_bits)985 Quantity DMath::encodeIeee754(const Quantity& val, const Quantity& exp_bits, const Quantity& significand_bits)
986 {
987     ENSURE_DIMENSIONLESS(val);
988     ENSURE_DIMENSIONLESS(exp_bits);
989     ENSURE_DIMENSIONLESS(significand_bits);
990 
991     Quantity result(CMath::encodeIeee754(val.numericValue(), exp_bits.numericValue(), significand_bits.numericValue()));
992     result.m_format = result.m_format + Quantity::Format::Fixed() + Quantity::Format::Hexadecimal();
993     return result;
994 }
995 
encodeIeee754(const Quantity & val,const Quantity & exp_bits,const Quantity & significand_bits,const Quantity & exp_bias)996 Quantity DMath::encodeIeee754(const Quantity& val, const Quantity& exp_bits, const Quantity& significand_bits,
997                               const Quantity& exp_bias)
998 {
999     ENSURE_DIMENSIONLESS(val);
1000     ENSURE_DIMENSIONLESS(exp_bits);
1001     ENSURE_DIMENSIONLESS(significand_bits);
1002     ENSURE_DIMENSIONLESS(exp_bias);
1003 
1004     Quantity result(CMath::encodeIeee754(val.numericValue(), exp_bits.numericValue(), significand_bits.numericValue(),
1005                                          exp_bias.numericValue()));
1006     result.m_format = result.m_format + Quantity::Format::Fixed() + Quantity::Format::Hexadecimal();
1007     return result;
1008 }
1009