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