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 }