File indexing completed on 2024-05-12 17:22:41

0001 // quantity.cpp
0002 // Support for units and dimensions
0003 //
0004 // This file is part of the SpeedCrunch project
0005 // Copyright (C) 2016 Pol Welter.
0006 // Copyright (C) 2016 @heldercorreia
0007 //
0008 // This program is free software; you can redistribute it and/or
0009 // modify it under the terms of the GNU General Public License
0010 // as published by the Free Software Foundation; either version 2
0011 // of the License, or (at your option) any later version.
0012 //
0013 // This program is distributed in the hope that it will be useful,
0014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
0015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0016 // GNU General Public License for more details.
0017 //
0018 // You should have received a copy of the GNU General Public License
0019 // along with this program; see the file COPYING.  If not, write to
0020 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0021 // Boston, MA 02110-1301, USA.
0022 
0023 
0024 #include "quantity.h"
0025 
0026 #include "rational.h"
0027 #include "units.h"
0028 
0029 #include <QStringList>
0030 
0031 #include <QLocale>
0032 #include <QDebug>
0033 
0034 #include <cmath>
0035 
0036 #define RATIONAL_TOL HNumber("1e-20")
0037 
0038 #define ENSURE_DIMENSIONLESS(x) \
0039     if (!(x).isDimensionless()) \
0040         return DMath::nan(InvalidDimension);
0041 
0042 #define ENSURE_SAME_DIMENSION(x, y) \
0043     if ((!(x).sameDimension(y))) \
0044         return DMath::nan(DimensionMismatch);
0045 
0046 Quantity operator-(const Quantity& q)
0047 {
0048     Quantity res(q);
0049     res.m_numericValue = -res.m_numericValue;
0050     return res;
0051 }
0052 
0053 Quantity operator-(const Quantity& a, const Quantity& b)
0054 {
0055     Quantity res(a);
0056     if (!a.sameDimension(b))
0057         return DMath::nan(DimensionMismatch);
0058     res.m_numericValue -= b.m_numericValue;
0059     return res;
0060 }
0061 
0062 bool operator>(const Quantity& l, const Quantity& r)
0063 {
0064     if (l.sameDimension(r))
0065         return l.m_numericValue > r.m_numericValue;
0066     return false;
0067 }
0068 
0069 bool operator<(const Quantity& l, const Quantity& r)
0070 {
0071     if (l.sameDimension(r))
0072         return l.m_numericValue < r.m_numericValue;
0073     return false;
0074 }
0075 
0076 bool operator>=(const Quantity& l, const Quantity& r)
0077 {
0078     if (l.sameDimension(r))
0079         return l.m_numericValue >= r.m_numericValue;
0080     return false;
0081 }
0082 
0083 bool operator<=(const Quantity& l, const Quantity& r)
0084 {
0085     if (l.sameDimension(r))
0086         return l.m_numericValue <= r.m_numericValue;
0087     return false;
0088 }
0089 
0090 bool operator==(const Quantity& l, const Quantity& r)
0091 {
0092     if (l.sameDimension(r))
0093         return l.m_numericValue == r.m_numericValue;
0094     return false;
0095 }
0096 
0097 // Returns TRUE upon dimension mismatch.
0098 bool operator!=(const Quantity& l, const Quantity& r)
0099 {
0100     if (l.sameDimension(r))
0101         return l.m_numericValue != r.m_numericValue;
0102     return true;
0103 }
0104 
0105 Quantity operator*(const HNumber& l, const Quantity& r)
0106 {
0107     return r * l;
0108 }
0109 
0110 Quantity operator*(const CNumber& l, const Quantity& r)
0111 {
0112     return r * l;
0113 }
0114 
0115 Quantity operator/(const HNumber& l, const Quantity& r)
0116 {
0117     return Quantity(l) / r;
0118 }
0119 
0120 Quantity operator/(const CNumber& l, const Quantity& r)
0121 {
0122     return Quantity(l) / r;
0123 }
0124 
0125 Quantity::Quantity()
0126     : m_numericValue(0)
0127     , m_unit(nullptr)
0128     , m_unitName("")
0129 {
0130 }
0131 
0132 Quantity::Quantity(const Quantity& other)
0133     : m_numericValue(other.m_numericValue)
0134     , m_dimension(other.m_dimension)
0135     , m_unit(nullptr)
0136     , m_unitName(other.m_unitName)
0137     , m_format(other.m_format)
0138 {
0139     if (other.hasUnit())
0140         this->m_unit = new CNumber(other.unit());
0141     cleanDimension();
0142 }
0143 
0144 Quantity::Quantity(int i)
0145     : Quantity(CNumber(i))
0146 {
0147 }
0148 
0149 Quantity::Quantity(const QJsonObject& json)
0150     : Quantity()
0151 {
0152     *this = deSerialize(json);
0153 }
0154 
0155 Quantity::Quantity(const HNumber& h)
0156     : Quantity(CNumber(h))
0157 {
0158 }
0159 
0160 Quantity::Quantity(const CNumber& c)
0161     : Quantity()
0162 {
0163     this->m_numericValue = c;
0164 }
0165 
0166 Quantity::~Quantity()
0167 {
0168     delete m_unit;
0169 }
0170 
0171 bool Quantity::isNan() const
0172 {
0173     return m_numericValue.isNan();
0174 }
0175 
0176 bool Quantity::isZero() const
0177 {
0178     return m_numericValue.isZero();
0179 }
0180 
0181 bool Quantity::isReal() const
0182 {
0183     return m_numericValue.isReal();
0184 }
0185 
0186 bool Quantity::isPositive() const
0187 {
0188     return m_numericValue.isPositive();
0189 }
0190 
0191 bool Quantity::isNegative() const
0192 {
0193     return m_numericValue.isNegative();
0194 }
0195 
0196 bool Quantity::isInteger() const
0197 {
0198     return (!this->hasDimension() && !this->hasUnit()) && m_numericValue.isInteger();
0199 }
0200 
0201 bool Quantity::hasUnit() const
0202 {
0203     return this->m_unit != NULL;
0204 }
0205 
0206 CNumber Quantity::unit() const
0207 {
0208     if (this->hasUnit())
0209         return CNumber(*(this->m_unit));
0210     return CNumber(1);
0211 }
0212 
0213 QString Quantity::unitName() const
0214 {
0215     if (this->hasUnit())
0216         return m_unitName;
0217     return "";
0218 }
0219 
0220 CNumber Quantity::numericValue() const
0221 {
0222     return m_numericValue;
0223 }
0224 
0225 Quantity& Quantity::setDisplayUnit(const CNumber unit, const QString& name)
0226 {
0227     if (unit.isNan())
0228         *this = DMath::nan(InvalidDimension);
0229     else {
0230         stripUnits();
0231         m_unit = new CNumber(unit);
0232         m_unitName = name;
0233     }
0234     return *this;
0235 }
0236 
0237 Quantity& Quantity::setFormat(Format c)
0238 {
0239     m_format = c;
0240     return *this;
0241 }
0242 
0243 void Quantity::stripUnits()
0244 {
0245     delete m_unit;
0246     m_unit = nullptr;
0247     m_unitName = "";
0248 }
0249 
0250 bool Quantity::hasDimension() const
0251 {
0252     return !this->m_dimension.empty();
0253 }
0254 
0255 /*
0256  * Unlike hasDimension(), this does a clean up first, i.e. it
0257  * checks for redundant exponents.
0258  */
0259 bool Quantity::isDimensionless() const
0260 {
0261     Quantity temp(*this);
0262     temp.cleanDimension();
0263     return m_dimension.empty();
0264 }
0265 
0266 QMap<QString, Rational> Quantity::getDimension() const
0267 {
0268     Quantity temp(*this);
0269     temp.cleanDimension();
0270     return temp.m_dimension;
0271 }
0272 
0273 void Quantity::modifyDimension(const QString& key, const Rational& exponent)
0274 {
0275     if (exponent.isZero())
0276         m_dimension.remove(key);
0277     else
0278         m_dimension.insert(key, exponent);
0279 }
0280 
0281 void Quantity::copyDimension(const Quantity& other)
0282 {
0283     clearDimension();
0284     this->m_dimension = other.m_dimension;
0285 }
0286 
0287 void Quantity::clearDimension()
0288 {
0289     this->m_dimension.clear();
0290 }
0291 
0292 // Note: Does NOT clean the dimension vector first.
0293 // The calling function must do so on its own.
0294 bool Quantity::sameDimension(const Quantity& other) const
0295 {
0296     return this->m_dimension == other.m_dimension;
0297 }
0298 
0299 void Quantity::cleanDimension()
0300 {
0301     auto i = m_dimension.begin();
0302     while (i != m_dimension.end()) {
0303         if (i.value().isZero())
0304             i = m_dimension.erase(i);
0305         else
0306             ++i;
0307     }
0308 }
0309 
0310 void Quantity::serialize(QJsonObject& json) const
0311 {
0312     QJsonObject nom_json;
0313     m_numericValue.serialize(nom_json);
0314     json["numeric_value"] = nom_json;
0315 
0316     if (hasDimension()) {
0317         QJsonObject dim_json;
0318         auto i = m_dimension.constBegin();
0319         while (i != m_dimension.constEnd()) {
0320             const auto& exp = i.value();
0321             const auto& name = i.key();
0322             dim_json[name] = exp.toString();
0323             ++i;
0324         }
0325         json["dimension"] = dim_json;
0326     }
0327 
0328     if (hasUnit()) {
0329         QJsonObject unit_json;
0330         m_unit->serialize(unit_json);
0331         json["unit"] = unit_json;
0332         json["unit_name"] = m_unitName;
0333     }
0334 
0335     if (!m_format.isNull()) {
0336         QJsonObject format_json;
0337         m_format.serialize(format_json);
0338         json["format"] = format_json;
0339     }
0340 }
0341 
0342 Quantity Quantity::deSerialize(const QJsonObject& json)
0343 {
0344     Quantity result;
0345     if (json.contains("numeric_value")) {
0346         QJsonObject num_json = json["numeric_value"].toObject();
0347         result.m_numericValue = CNumber(num_json);
0348     }
0349     result.stripUnits();
0350     if (json.contains("unit")) {
0351         QJsonObject unit_json = json["unit"].toObject();
0352         result.m_unit = new CNumber(unit_json);
0353     }
0354     if (json.contains("unit_name"))
0355         result.m_unitName = json["unit_name"].toString();
0356 
0357     if (json.contains("dimension")) {
0358         QJsonObject dim_json = json["dimension"].toObject();
0359         for (int i = 0; i < dim_json.count(); ++i) {
0360             auto key = dim_json.keys().at(i);
0361             Rational val(dim_json[key].toString());
0362             result.modifyDimension(key, val);
0363         }
0364     }
0365     if (json.contains("format")) {
0366         QJsonObject format_json = json["format"].toObject();
0367         result.m_format = Quantity::Format::deSerialize(format_json);
0368     }
0369     return result;
0370 }
0371 
0372 Error Quantity::error() const
0373 {
0374     return m_numericValue.error();
0375 }
0376 
0377 Quantity& Quantity::operator=(const Quantity& other)
0378 {
0379     m_numericValue = other.m_numericValue;
0380     m_dimension = other.m_dimension;
0381     m_format = other.m_format;
0382     stripUnits();
0383     if(other.hasUnit()) {
0384         m_unit = new CNumber(*other.m_unit);
0385         m_unitName = other.m_unitName;
0386     }
0387     cleanDimension();
0388     return *this;
0389 }
0390 
0391 Quantity Quantity::operator+(const Quantity& other) const
0392 {
0393     if (!this->sameDimension(other))
0394         return DMath::nan(DimensionMismatch);
0395     Quantity result(*this);
0396     result.m_numericValue += other.m_numericValue;
0397     return result;
0398 }
0399 
0400 Quantity& Quantity::operator+=(const Quantity& other)
0401 {
0402     if (!this->sameDimension(other))
0403         *this = DMath::nan(DimensionMismatch);
0404     else
0405         this->m_numericValue += other.m_numericValue;
0406     return *this;
0407 }
0408 
0409 Quantity& Quantity::operator-=(const Quantity& other)
0410 {
0411     return operator=(*this - other);
0412 }
0413 
0414 Quantity Quantity::operator*(const Quantity& other) const
0415 {
0416     Quantity result(*this);
0417     result.m_numericValue *= other.m_numericValue;
0418     if (!other.isDimensionless()) {
0419         result.stripUnits();
0420         auto i = other.m_dimension.constBegin();
0421         while (i != other.m_dimension.constEnd()) {
0422             const auto& exp = i.value();
0423             const auto& name = i.key();
0424             if (!result.m_dimension.contains(name))
0425                 result.m_dimension[name] = Rational(0);
0426             result.m_dimension[name] += exp;
0427             ++i;
0428         }
0429         result.cleanDimension();
0430     }
0431     return result;
0432 }
0433 
0434 Quantity Quantity::operator*(const CNumber& other) const
0435 {
0436     Quantity result(*this);
0437     result.m_numericValue *= other;
0438     return result;
0439 }
0440 
0441 Quantity Quantity::operator*(const HNumber& other) const
0442 {
0443     return operator*(CNumber(other));
0444 }
0445 
0446 Quantity &Quantity::operator*=(const Quantity& other)
0447 {
0448     return operator=(*this * other);
0449 }
0450 
0451 Quantity Quantity::operator/(const Quantity& other) const
0452 {
0453     Quantity result(*this);
0454     result.m_numericValue /= other.m_numericValue;
0455     if (!other.isDimensionless()) {
0456         result.stripUnits();
0457         auto i = other.m_dimension.constBegin();
0458         while (i != other.m_dimension.constEnd()) {
0459             const auto& exp = i.value();
0460             const auto& name = i.key();
0461             if (!result.m_dimension.contains(name))
0462                 result.m_dimension[name] = Rational(0);
0463             result.m_dimension[name] -= exp;
0464             ++i;
0465         }
0466         result.cleanDimension();
0467     }
0468     return result;
0469 }
0470 
0471 Quantity Quantity::operator/(const HNumber& other) const
0472 {
0473     return operator/(CNumber(other));
0474 }
0475 
0476 Quantity Quantity::operator/(const CNumber& other) const
0477 {
0478     Quantity result(*this);
0479     result.m_numericValue /= other;
0480     result.cleanDimension();
0481     return result;
0482 }
0483 
0484 Quantity &Quantity::operator/=(const Quantity& other)
0485 {
0486     return operator=(*this/other);
0487 }
0488 
0489 Quantity Quantity::operator%(const Quantity& other) const
0490 {
0491     Quantity result(*this);
0492     result.m_numericValue = result.m_numericValue % other.m_numericValue;
0493     return result;
0494 }
0495 
0496 Quantity Quantity::operator&(const Quantity& other) const
0497 {
0498     ENSURE_DIMENSIONLESS(*this);
0499     ENSURE_DIMENSIONLESS(other);
0500     Quantity result(*this);
0501     result.m_numericValue &= other.m_numericValue;
0502     return result;
0503 }
0504 
0505 Quantity &Quantity::operator&=(const Quantity& other)
0506 {
0507     return operator=(*this & other);
0508 }
0509 
0510 Quantity Quantity::operator|(const Quantity& other) const
0511 {
0512     ENSURE_DIMENSIONLESS(*this);
0513     ENSURE_DIMENSIONLESS(other);
0514     Quantity result(*this);
0515     result.m_numericValue |= other.m_numericValue;
0516     return result;
0517 }
0518 
0519 Quantity &Quantity::operator|=(const Quantity& other)
0520 {
0521     return operator=(*this | other);
0522 }
0523 
0524 Quantity Quantity::operator^(const Quantity& other) const
0525 {
0526     ENSURE_DIMENSIONLESS(*this);
0527     ENSURE_DIMENSIONLESS(other);
0528     Quantity result(*this);
0529     result.m_numericValue ^= other.m_numericValue;
0530     return result;
0531 }
0532 
0533 Quantity &Quantity::operator^=(const Quantity& other)
0534 {
0535     return operator=(*this ^ other);
0536 }
0537 
0538 Quantity Quantity::operator~() const
0539 {
0540     ENSURE_DIMENSIONLESS(*this);
0541     Quantity result(*this);
0542     result.m_numericValue= ~result.m_numericValue;
0543     return result;
0544 }
0545 
0546 Quantity Quantity::operator>>(const Quantity& other) const
0547 {
0548     ENSURE_DIMENSIONLESS(*this);
0549     ENSURE_DIMENSIONLESS(other);
0550     Quantity result(*this);
0551     result.m_numericValue = result.m_numericValue >> other.m_numericValue;
0552     return result;
0553 }
0554 
0555 Quantity Quantity::operator<<(const Quantity& other) const
0556 {
0557     ENSURE_DIMENSIONLESS(*this);
0558     ENSURE_DIMENSIONLESS(other);
0559     Quantity result(*this);
0560     result.m_numericValue = result.m_numericValue << other.m_numericValue;
0561     return result;
0562 }
0563 
0564 Quantity::Format::Format()
0565     : CNumber::Format()
0566 {
0567 }
0568 
0569 Quantity::Format::Format(const CNumber::Format& other)
0570     : CNumber::Format(other)
0571 {
0572 }
0573 
0574 Quantity::Format::Format(const HNumber::Format& other)
0575     : CNumber::Format(other)
0576 {
0577 }
0578 
0579 Quantity::Format Quantity::Format::operator+(const Quantity::Format& other) const
0580 {
0581     return Quantity::Format(CNumber::Format::operator+(static_cast<const CNumber::Format&>(other)));
0582 }
0583 
0584 void Quantity::Format::serialize(QJsonObject& json) const
0585 {
0586     switch (mode) {
0587     case Mode::General:
0588         json["mode"] = QStringLiteral("General");
0589         break;
0590     case Mode::Fixed:
0591         json["mode"] = QStringLiteral("Fixed");
0592         break;
0593     case Mode::Scientific:
0594         json["mode"] = QStringLiteral("Scientific");
0595         break;
0596     case Mode::Engineering:
0597         json["mode"] = QStringLiteral("Engineering");
0598         break;
0599     case Mode::Null:
0600         break;
0601     }
0602 
0603     switch (base) {
0604     case Base::Binary:
0605         json["base"] = QStringLiteral("Binary");
0606         break;
0607     case Base::Octal:
0608         json["base"] = QStringLiteral("Octal");
0609         break;
0610     case Base::Hexadecimal:
0611         json["base"] = QStringLiteral("Hexadecimal");
0612         break;
0613     case Base::Decimal:
0614         json["base"] = QStringLiteral("Decimal");
0615         break;
0616     case Base::Null:
0617         break;
0618     }
0619 
0620     switch (notation) {
0621     case Notation::Cartesian:
0622         json["form"] = QStringLiteral("Cartesian");
0623         break;
0624     case Notation::Polar:
0625         json["form"] = QStringLiteral("Polar");
0626         break;
0627     case Notation::Null:
0628     default:
0629         break;
0630     }
0631 
0632     if (precision != PrecisionNull)
0633         json["precision"] = precision;
0634 }
0635 
0636 Quantity::Format Quantity::Format::deSerialize(const QJsonObject& json)
0637 {
0638     Format result;
0639     if (json.contains("mode")) {
0640         auto strMode = json["mode"].toString();
0641         if (strMode == "General")
0642             result.mode = Mode::General;
0643         else if (strMode == "Fixed")
0644             result.mode = Mode::Fixed;
0645         else if (strMode == "Scientific")
0646             result.mode = Mode::Scientific;
0647         else if (strMode == "Engineering")
0648             result.mode = Mode::Engineering;
0649         else
0650             result.mode = Mode::Null;
0651     } else
0652         result.mode = Mode::Null;
0653 
0654     if (json.contains("base")) {
0655         auto strBase = json["base"].toString();
0656         if (strBase == "Binary")
0657             result.base = Base::Binary;
0658         else if (strBase == "Octal")
0659             result.base = Base::Octal;
0660         else if (strBase == "Decimal")
0661             result.base = Base::Decimal;
0662         else if (strBase == "Hexadecimal")
0663             result.base = Base::Hexadecimal;
0664         else
0665             result.base = Base::Null;
0666     } else
0667         result.base = Base::Null;
0668 
0669     if (json.contains("form")) {
0670         auto strNotation = json["form"].toString();
0671         if (strNotation == "Cartesian")
0672             result.notation = Notation::Cartesian;
0673         else if (strNotation == "Polar")
0674             result.notation = Notation::Polar;
0675         else
0676             result.notation = Notation::Null;
0677     } else
0678         result.notation = Notation::Null;
0679 
0680 
0681     result.precision = json.contains("precision") ? json["precision"].toInt() : PrecisionNull;
0682     return result;
0683 }
0684 
0685 bool Quantity::Format::isNull() const
0686 {
0687     return (mode == Mode::Null && base == Base::Null && precision == PrecisionNull && notation == Notation::Null);
0688 }
0689 
0690 // DMath
0691 // =====
0692 
0693 bool DMath::complexMode = true;
0694 
0695 #define COMPLEX_WRAP_1(fct, arg) \
0696     (DMath::complexMode ? CMath::fct(arg) : CNumber(HMath::fct(arg.real)))
0697 
0698 #define COMPLEX_WRAP_2(fct, arg1, arg2) \
0699     (DMath::complexMode ? CMath::fct(arg1, arg2) : CNumber(HMath::fct(arg1.real, arg2.real)))
0700 
0701 #define COMPLEX_WRAP_3(fct, arg1, arg2, arg3) \
0702     (DMath::complexMode ? CMath::fct(arg1, arg2, arg3) : CNumber(HMath::fct(arg1.real, arg2.real, arg3.real)))
0703 
0704 #define COMPLEX_WRAP_4(fct, arg1, arg2, arg3, arg4) \
0705     (DMath::complexMode ? CMath::fct(arg1, arg2, arg3, arg4) \
0706     : CNumber(HMath::fct(arg1.real, arg2.real, arg3.real, arg4.real)))
0707 
0708 //  Wrappers for functions that are only defined for dimensionless arguments
0709 
0710 // Mo argument.
0711 #define WRAPPER_DMATH_0(fct) \
0712     Quantity DMath::fct() \
0713     { \
0714         return Quantity(CMath::fct()); \
0715     } \
0716 
0717 // One argument.
0718 #define WRAPPER_DMATH_1(fct) \
0719     Quantity DMath::fct(const Quantity& arg1) \
0720     { \
0721         ENSURE_DIMENSIONLESS(arg1); \
0722         return Quantity(COMPLEX_WRAP_1(fct, arg1.m_numericValue)); \
0723     }
0724 
0725 // Two arguments.
0726 #define WRAPPER_DMATH_2(fct) \
0727     Quantity DMath::fct(const Quantity& arg1, const Quantity& arg2) \
0728     { \
0729         ENSURE_DIMENSIONLESS(arg1); \
0730         ENSURE_DIMENSIONLESS(arg2); \
0731         return Quantity(COMPLEX_WRAP_2(fct, arg1.m_numericValue, arg2.m_numericValue)); \
0732     }
0733 
0734 // Three arguments.
0735 #define WRAPPER_DMATH_3(fct) \
0736     Quantity DMath::fct(const Quantity& arg1, const Quantity& arg2, const Quantity& arg3) \
0737     { \
0738         ENSURE_DIMENSIONLESS(arg1); \
0739         ENSURE_DIMENSIONLESS(arg2); \
0740         ENSURE_DIMENSIONLESS(arg3); \
0741         return Quantity(COMPLEX_WRAP_3(fct, arg1.m_numericValue, arg2.m_numericValue, arg3.m_numericValue)); \
0742     }
0743 
0744 // Four arguments.
0745 #define WRAPPER_DMATH_4(fct) \
0746     Quantity DMath::fct(const Quantity& arg1, const Quantity& arg2, const Quantity& arg3, const Quantity& arg4) \
0747     { \
0748         ENSURE_DIMENSIONLESS(arg1); \
0749         ENSURE_DIMENSIONLESS(arg2); \
0750         ENSURE_DIMENSIONLESS(arg3); \
0751         ENSURE_DIMENSIONLESS(arg4); \
0752         return Quantity(COMPLEX_WRAP_4(fct, arg1.m_numericValue, arg2.m_numericValue, arg3.m_numericValue, \
0753                                        arg4.m_numericValue)); \
0754     }
0755 
0756 WRAPPER_DMATH_0(e)
0757 WRAPPER_DMATH_0(pi)
0758 WRAPPER_DMATH_0(phi)
0759 WRAPPER_DMATH_0(i)
0760 
0761 Quantity DMath::nan(Error error)
0762 {
0763     return Quantity(CMath::nan(error));
0764 }
0765 
0766 WRAPPER_DMATH_1(rad2deg)
0767 WRAPPER_DMATH_1(deg2rad)
0768 WRAPPER_DMATH_1(rad2gon)
0769 WRAPPER_DMATH_1(gon2rad)
0770 WRAPPER_DMATH_1(integer)
0771 WRAPPER_DMATH_1(frac)
0772 WRAPPER_DMATH_1(floor)
0773 WRAPPER_DMATH_1(ceil)
0774 WRAPPER_DMATH_1(exp)
0775 WRAPPER_DMATH_1(ln)
0776 WRAPPER_DMATH_1(lg)
0777 WRAPPER_DMATH_1(lb)
0778 WRAPPER_DMATH_2(log)
0779 WRAPPER_DMATH_1(sinh)
0780 WRAPPER_DMATH_1(cosh)
0781 WRAPPER_DMATH_1(tanh)
0782 WRAPPER_DMATH_1(arsinh)
0783 WRAPPER_DMATH_1(arcosh)
0784 WRAPPER_DMATH_1(artanh)
0785 WRAPPER_DMATH_1(sin)
0786 WRAPPER_DMATH_1(cos)
0787 WRAPPER_DMATH_1(tan)
0788 WRAPPER_DMATH_1(cot)
0789 WRAPPER_DMATH_1(sec)
0790 WRAPPER_DMATH_1(csc)
0791 WRAPPER_DMATH_1(arcsin)
0792 WRAPPER_DMATH_1(arccos)
0793 WRAPPER_DMATH_1(arctan)
0794 WRAPPER_DMATH_2(arctan2)
0795 
0796 WRAPPER_DMATH_2(factorial)
0797 WRAPPER_DMATH_1(gamma)
0798 WRAPPER_DMATH_1(lnGamma)
0799 WRAPPER_DMATH_1(erf)
0800 WRAPPER_DMATH_1(erfc)
0801 
0802 WRAPPER_DMATH_2(gcd)
0803 WRAPPER_DMATH_2(idiv)
0804 
0805 Quantity DMath::round(const Quantity& n, int prec)
0806 {
0807     ENSURE_DIMENSIONLESS(n);
0808     return DMath::complexMode ?
0809         CMath::round(n.numericValue(), prec) :
0810         CNumber(HMath::round(n.numericValue().real, prec));
0811 }
0812 
0813 Quantity DMath::trunc(const Quantity& n, int prec)
0814 {
0815     ENSURE_DIMENSIONLESS(n);
0816     return DMath::complexMode ?
0817         CMath::trunc(n.numericValue(), prec)
0818         : CNumber(HMath::trunc(n.numericValue().real, prec));
0819 }
0820 
0821 WRAPPER_DMATH_2(nCr)
0822 WRAPPER_DMATH_2(nPr)
0823 WRAPPER_DMATH_3(binomialPmf)
0824 WRAPPER_DMATH_3(binomialCdf)
0825 WRAPPER_DMATH_2(binomialMean)
0826 WRAPPER_DMATH_2(binomialVariance)
0827 WRAPPER_DMATH_4(hypergeometricPmf)
0828 WRAPPER_DMATH_4(hypergeometricCdf)
0829 WRAPPER_DMATH_3(hypergeometricMean)
0830 WRAPPER_DMATH_3(hypergeometricVariance)
0831 WRAPPER_DMATH_2(poissonPmf)
0832 WRAPPER_DMATH_2(poissonCdf)
0833 WRAPPER_DMATH_1(poissonMean)
0834 WRAPPER_DMATH_1(poissonVariance)
0835 
0836 WRAPPER_DMATH_2(mask)
0837 WRAPPER_DMATH_2(sgnext)
0838 WRAPPER_DMATH_2(ashr)
0839 
0840 
0841 WRAPPER_DMATH_3(decodeIeee754)
0842 WRAPPER_DMATH_4(decodeIeee754)
0843 
0844 
0845 QString DMath::format(Quantity q, Quantity::Format format)
0846 {
0847     format = q.format() + format;  // Left hand side oerator takes priority.
0848 
0849     // Handle units.
0850     if (!q.hasUnit() && !q.isDimensionless()) {
0851         q.cleanDimension();
0852         Units::findUnit(q);
0853     }
0854     QString unit_name = ' ' + q.unitName();
0855     CNumber unit = q.unit();
0856     CNumber number = q.m_numericValue;
0857 
0858     number /= unit;
0859 
0860     QString result;
0861 
0862     result = CMath::format(number, format);
0863 
0864     result.replace('-', QString::fromUtf8("−"));
0865 
0866     // Replace all spaces between units with dot operator.
0867     int emptySpaces = 0;
0868     for (QChar& ch : result) {
0869         if (ch.isSpace()) {
0870             ++emptySpaces;
0871             if (emptySpaces > 1) {
0872                 ch = u'⋅';
0873             }
0874         }
0875     }
0876 
0877     // '.' is hardcoded everywhere
0878     if (format.mode == Quantity::Format::Mode::Fixed) {
0879         result.replace('.', QLocale::system().decimalPoint());
0880     }
0881 
0882     if (!number.real.isZero() && !number.imag.isZero() && unit_name != " ")
0883         result = "(" + result + ")";
0884 
0885     if (unit_name != " ")
0886         result.append(unit_name);
0887 
0888     return result;
0889 }
0890 
0891 Quantity DMath::real(const Quantity& x)
0892 {
0893     Quantity result(x);
0894     result.m_numericValue = result.m_numericValue.real;
0895     return result;
0896 }
0897 
0898 Quantity DMath::imag(const Quantity& x)
0899 {
0900     Quantity result(x);
0901     result.m_numericValue = result.m_numericValue.imag;
0902     return result;
0903 }
0904 
0905 Quantity DMath::conj(const Quantity& n)
0906 {
0907     Quantity result = Quantity(n);
0908     // If in Real mode, just strip the imaginary part.
0909     result.m_numericValue = complexMode ?
0910         CMath::conj(result.m_numericValue)
0911         : CMath::real(result.m_numericValue);
0912 
0913     return result;
0914 }
0915 
0916 Quantity DMath::abs(const Quantity& n)
0917 {
0918     Quantity result(n);
0919     result.m_numericValue = COMPLEX_WRAP_1(abs, n.m_numericValue);
0920     return result;
0921 }
0922 
0923 Quantity DMath::phase(const Quantity& n)
0924 {
0925     return CMath::phase(n.numericValue());
0926 }
0927 
0928 Quantity DMath::sqrt(const Quantity& n)
0929 {
0930     Quantity result(COMPLEX_WRAP_1(sqrt, n.m_numericValue));
0931     auto i = n.m_dimension.constBegin();
0932     while (i != n.m_dimension.constEnd()) {
0933         auto& exp = i.value();
0934         auto& name = i.key();
0935         result.modifyDimension(name, exp * Rational(1,2));
0936         ++i;
0937     }
0938     return result;
0939 }
0940 
0941 Quantity DMath::cbrt(const Quantity& n)
0942 {
0943     Quantity result(COMPLEX_WRAP_1(cbrt, n.m_numericValue));
0944     auto i = n.m_dimension.constBegin();
0945     while (i != n.m_dimension.constEnd()) {
0946         auto& exp = i.value();
0947         auto& name = i.key();
0948         result.modifyDimension(name, exp * Rational(1,3));
0949         ++i;
0950     }
0951     return result;
0952 }
0953 
0954 Quantity DMath::raise(const Quantity& n1, int n)
0955 {
0956     Quantity result;
0957     result.m_numericValue = complexMode ?
0958         CMath::raise(n1.m_numericValue, n)
0959         : CNumber(HMath::raise(n1.m_numericValue.real, n));
0960     auto i = n1.m_dimension.constBegin();
0961     while (i != n1.m_dimension.constEnd()) {
0962         auto& exp = i.value();
0963         auto& name = i.key();
0964         result.modifyDimension(name, exp * n);
0965         ++i;
0966     }
0967     return result;
0968 }
0969 
0970 Quantity DMath::raise(const Quantity& n1, const Quantity& n2)
0971 {
0972     if (!n2.isDimensionless() || (!n1.isDimensionless() && !n2.isReal() && complexMode))
0973         return DMath::nan(InvalidDimension);
0974 
0975     // First get the new numeric value.
0976     Quantity result(COMPLEX_WRAP_2(raise, n1.m_numericValue, n2.m_numericValue));
0977 
0978     if (n1.isDimensionless())
0979         return result;
0980 
0981     // We can now assume that n1 has a dimension, but n2 is real.
0982     // Compute the new dimension: try to convert n2 to a Rational. If n2 is not
0983     // rational, return NaN.
0984 
0985     // For negative bases only allow odd denominators.
0986     Rational exponent(n2.m_numericValue.real);
0987     if (abs(exponent.toHNumber() - n2.m_numericValue.real) >= RATIONAL_TOL
0988        || (n1.isNegative() && exponent.denominator() % 2 == 0))
0989         return DMath::nan(OutOfDomain);
0990 
0991     // Compute new dimension.
0992     auto i = n1.m_dimension.constBegin();
0993     while (i != n1.m_dimension.constEnd()) {
0994         result.modifyDimension(i.key(), i.value()*exponent);
0995         ++i;
0996     }
0997     return result;
0998 }
0999 
1000 Quantity DMath::sgn(const Quantity& x)
1001 {
1002     return Quantity(CMath::sgn(x.m_numericValue));
1003 }
1004 
1005 Quantity DMath::encodeIeee754(const Quantity& val, const Quantity& exp_bits, const Quantity& significand_bits)
1006 {
1007     ENSURE_DIMENSIONLESS(val);
1008     ENSURE_DIMENSIONLESS(exp_bits);
1009     ENSURE_DIMENSIONLESS(significand_bits);
1010 
1011     Quantity result(CMath::encodeIeee754(val.numericValue(), exp_bits.numericValue(), significand_bits.numericValue()));
1012     result.m_format = result.m_format + Quantity::Format::Fixed() + Quantity::Format::Hexadecimal();
1013     return result;
1014 }
1015 
1016 Quantity DMath::encodeIeee754(const Quantity& val, const Quantity& exp_bits, const Quantity& significand_bits,
1017                               const Quantity& exp_bias)
1018 {
1019     ENSURE_DIMENSIONLESS(val);
1020     ENSURE_DIMENSIONLESS(exp_bits);
1021     ENSURE_DIMENSIONLESS(significand_bits);
1022     ENSURE_DIMENSIONLESS(exp_bias);
1023 
1024     Quantity result(CMath::encodeIeee754(val.numericValue(), exp_bits.numericValue(), significand_bits.numericValue(),
1025                                          exp_bias.numericValue()));
1026     result.m_format = result.m_format + Quantity::Format::Fixed() + Quantity::Format::Hexadecimal();
1027     return result;
1028 }