• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2020-2024 Stefan Krah. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 
28 #ifndef LIBMPDECXX_DECIMAL_HH_
29 #define LIBMPDECXX_DECIMAL_HH_
30 
31 
32 #include <cassert>
33 #include <cstddef>
34 #include <cstdint>
35 
36 #include <algorithm>
37 #include <atomic>
38 #include <iostream>
39 #include <limits>
40 #include <memory>
41 #include <stdexcept>
42 #include <string>
43 #include <type_traits>
44 #include <utility>
45 
46 #include <mpdecimal.h>
47 
48 #undef iscanonical /* math.h */
49 #undef isfinite    /* math.h */
50 #undef isinf       /* math.h */
51 #undef isnan       /* math.h */
52 #undef isnormal    /* math.h */
53 #undef issubnormal /* math.h */
54 #undef iszero      /* math.h */
55 #undef isspecial   /* ctype.h */
56 
57 #undef IMPORTEXPORT
58 #ifdef _MSC_VER
59   #if defined (BUILD_LIBMPDECXX)
60     #define IMPORTEXPORT __declspec(dllexport)
61   #elif defined(_DLL)
62     #define IMPORTEXPORT __declspec(dllimport)
63   #else
64     #define IMPORTEXPORT
65   #endif
66   #define ALWAYS_INLINE __forceinline
67 #else
68   #define IMPORTEXPORT
69   #define ALWAYS_INLINE inline
70 #endif
71 
72 
73 namespace decimal {
74 
75 /******************************************************************************/
76 /*                          Constants from libmpdec                           */
77 /******************************************************************************/
78 
79 enum round {
80   ROUND_UP = MPD_ROUND_UP,               /* round away from 0               */
81   ROUND_DOWN = MPD_ROUND_DOWN,           /* round toward 0 (truncate)       */
82   ROUND_CEILING = MPD_ROUND_CEILING,     /* round toward +infinity          */
83   ROUND_FLOOR = MPD_ROUND_FLOOR,         /* round toward -infinity          */
84   ROUND_HALF_UP = MPD_ROUND_HALF_UP,     /* 0.5 is rounded up               */
85   ROUND_HALF_DOWN = MPD_ROUND_HALF_DOWN, /* 0.5 is rounded down             */
86   ROUND_HALF_EVEN = MPD_ROUND_HALF_EVEN, /* 0.5 is rounded to even          */
87   ROUND_05UP = MPD_ROUND_05UP,           /* round zero or five away from 0  */
88   ROUND_TRUNC = MPD_ROUND_TRUNC,         /* truncate, but set infinity      */
89   ROUND_GUARD = MPD_ROUND_GUARD
90 };
91 
92 /*
93  * Aliases for a spelling that is consistent with the exceptions below.
94  * Use whichever form you prefer.
95  */
96 constexpr uint32_t DecClamped = MPD_Clamped;
97 constexpr uint32_t DecConversionSyntax = MPD_Conversion_syntax;
98 constexpr uint32_t DecDivisionByZero = MPD_Division_by_zero;
99 constexpr uint32_t DecDivisionImpossible = MPD_Division_impossible;
100 constexpr uint32_t DecDivisionUndefined = MPD_Division_undefined;
101 constexpr uint32_t DecFpuError = MPD_Fpu_error; /* unused */
102 constexpr uint32_t DecInexact = MPD_Inexact;
103 constexpr uint32_t DecInvalidContext = MPD_Invalid_context;
104 constexpr uint32_t DecInvalidOperation = MPD_Invalid_operation;
105 constexpr uint32_t DecMallocError = MPD_Malloc_error;
106 constexpr uint32_t DecNotImplemented = MPD_Not_implemented; /* unused */
107 constexpr uint32_t DecOverflow = MPD_Overflow;
108 constexpr uint32_t DecRounded = MPD_Rounded;
109 constexpr uint32_t DecSubnormal = MPD_Subnormal;
110 constexpr uint32_t DecUnderflow = MPD_Underflow;
111 
112 /* Flag sets */
113 constexpr uint32_t DecIEEEInvalidOperation = MPD_IEEE_Invalid_operation;
114                                             /* DecConversionSyntax */
115                                             /* DecDivisionImpossible */
116                                             /* DecDivisionUndefined */
117                                             /* DecFpuError */
118                                             /* DecInvalidContext */
119                                             /* DecInvalidOperation */
120                                             /* DecMallocError */
121 
122 constexpr uint32_t DecErrors = MPD_Errors;
123                                /* DecIEEEInvalidOperation */
124                                /* DecDivisionByZero */
125 
126 constexpr uint32_t DecMaxStatus = MPD_Max_status;
127                                   /* All flags */
128 
129 /* IEEEContext(): common arguments */
130 constexpr int DECIMAL32 = MPD_DECIMAL32;
131 constexpr int DECIMAL64 = MPD_DECIMAL64;
132 constexpr int DECIMAL128 = MPD_DECIMAL128;
133 
134 /* IEEEContext(): maximum argument value */
135 constexpr int IEEE_CONTEXT_MAX_BITS = MPD_IEEE_CONTEXT_MAX_BITS;
136 
137 
138 /******************************************************************************/
139 /*                             Decimal Exceptions                             */
140 /******************************************************************************/
141 
142 class DecimalException : public std::runtime_error {
143   using std::runtime_error::runtime_error;
144 };
145 
146 /* Signals */
147 class IEEEInvalidOperation : public DecimalException {
148   using DecimalException::DecimalException;
149 };
150 
151 class DivisionByZero : public DecimalException {
152   using DecimalException::DecimalException;
153 };
154 
155 class Overflow : public DecimalException {
156   using DecimalException::DecimalException;
157 };
158 
159 class Underflow : public DecimalException {
160   using DecimalException::DecimalException;
161 };
162 
163 class Subnormal : public DecimalException {
164   using DecimalException::DecimalException;
165 };
166 
167 class Inexact : public DecimalException {
168   using DecimalException::DecimalException;
169 };
170 
171 class Rounded : public DecimalException {
172   using DecimalException::DecimalException;
173 };
174 
175 class Clamped : public DecimalException {
176   using DecimalException::DecimalException;
177 };
178 
179 /* Conditions */
180 class InvalidOperation : public IEEEInvalidOperation {
181   using IEEEInvalidOperation::IEEEInvalidOperation;
182 };
183 
184 class ConversionSyntax : public IEEEInvalidOperation {
185   using IEEEInvalidOperation::IEEEInvalidOperation;
186 };
187 
188 class DivisionImpossible : public IEEEInvalidOperation {
189   using IEEEInvalidOperation::IEEEInvalidOperation;
190 };
191 
192 class DivisionUndefined : public IEEEInvalidOperation {
193   using IEEEInvalidOperation::IEEEInvalidOperation;
194 };
195 
196 
197 /******************************************************************************/
198 /*                               Other Exceptions                             */
199 /******************************************************************************/
200 
201 class MallocError : public DecimalException {
202   using DecimalException::DecimalException;
203 };
204 
205 class RuntimeError : public DecimalException {
206   using DecimalException::DecimalException;
207 };
208 
209 class ValueError : public DecimalException {
210   using DecimalException::DecimalException;
211 };
212 
213 
214 /******************************************************************************/
215 /*                              Context object                                */
216 /******************************************************************************/
217 
218 class Context;
219 
220 IMPORTEXPORT extern Context context_template;
221 #if defined(__OpenBSD__) || defined(__sun) || defined(_MSC_VER) && defined(_DLL)
222 IMPORTEXPORT Context& getcontext();
223 static thread_local Context& context{getcontext()};
224 #else
225 extern thread_local Context context;
226 #endif
227 
228 class Context {
229  private:
230   mpd_context_t ctx;
231   IMPORTEXPORT static void raiseit(uint32_t status);
232 
233  public:
234   /***********************************************************************/
235   /*                             Constructors                            */
236   /***********************************************************************/
237   Context(const Context& c) = default;
238   Context(Context&& c) = default;
239 
Context(const mpd_context_t & m)240   explicit Context(const mpd_context_t &m) noexcept : ctx(m) {}
241 
Context(mpd_ssize_t prec=context_template.prec (),mpd_ssize_t emax=context_template.emax (),mpd_ssize_t emin=context_template.emin (),int round=context_template.round (),uint32_t traps=context_template.traps (),int clamp=context_template.clamp (),int allcr=context_template.allcr ())242   explicit Context(mpd_ssize_t prec=context_template.prec(),
243                    mpd_ssize_t emax=context_template.emax(),
244                    mpd_ssize_t emin=context_template.emin(),
245                    int round=context_template.round(),
246                    uint32_t traps=context_template.traps(),
247                    int clamp=context_template.clamp(),
248                    int allcr=context_template.allcr()) {
249     this->prec(prec);
250     this->emax(emax);
251     this->emin(emin);
252     this->traps(traps);
253     this->round(round);
254     this->clamp(clamp);
255     this->allcr(allcr);
256     this->ctx.status = 0;
257     this->ctx.newtrap = 0;
258   }
259 
260   /***********************************************************************/
261   /*                             Destructor                              */
262   /***********************************************************************/
263   ~Context() noexcept = default;
264 
265   /***********************************************************************/
266   /*                         Assignment operators                        */
267   /***********************************************************************/
268   Context& operator= (const Context& c) = default;
269   Context& operator= (Context&& c) = default;
270 
271   /***********************************************************************/
272   /*                         Comparison operators                        */
273   /***********************************************************************/
operator ==(const Context & other) const274   bool operator== (const Context& other) const noexcept {
275     return ctx.prec == other.ctx.prec &&
276            ctx.emax == other.ctx.emax &&
277            ctx.emin == other.ctx.emin &&
278            ctx.traps == other.ctx.traps &&
279            ctx.status == other.ctx.status &&
280            ctx.round == other.ctx.round &&
281            ctx.clamp == other.ctx.clamp &&
282            ctx.allcr == other.ctx.allcr &&
283            ctx.newtrap == other.ctx.newtrap;
284   }
285 
operator !=(const Context & other) const286   bool operator!= (const Context& other) const noexcept {
287     return !(*this == other);
288   }
289 
290   /***********************************************************************/
291   /*                              Accessors                              */
292   /***********************************************************************/
293   /* Get pointers to the full context */
get()294   ALWAYS_INLINE mpd_context_t *get() { return &ctx; }
getconst() const295   ALWAYS_INLINE const mpd_context_t *getconst() const { return &ctx; }
296 
297   /* Get individual fields */
prec() const298   ALWAYS_INLINE mpd_ssize_t prec() const { return ctx.prec; }
emax() const299   ALWAYS_INLINE mpd_ssize_t emax() const { return ctx.emax; }
emin() const300   ALWAYS_INLINE mpd_ssize_t emin() const { return ctx.emin; }
round() const301   ALWAYS_INLINE int round() const { return ctx.round; }
status() const302   ALWAYS_INLINE uint32_t status() const { return ctx.status; }
traps() const303   ALWAYS_INLINE uint32_t traps() const { return ctx.traps; }
clamp() const304   ALWAYS_INLINE int clamp() const { return ctx.clamp; }
allcr() const305   ALWAYS_INLINE int allcr() const { return ctx.allcr; }
etiny() const306   ALWAYS_INLINE int64_t etiny() const { return mpd_etiny(&ctx); }
etop() const307   ALWAYS_INLINE int64_t etop() const { return mpd_etop(&ctx); }
308 
309   /* Set individual fields */
prec(mpd_ssize_t v)310   ALWAYS_INLINE void prec(mpd_ssize_t v) {
311     if (!mpd_qsetprec(&ctx, v)) {
312       throw ValueError("valid range for prec is [1, MAX_PREC]");
313     }
314   }
315 
emax(mpd_ssize_t v)316   ALWAYS_INLINE void emax(mpd_ssize_t v) {
317     if (!mpd_qsetemax(&ctx, v)) {
318       throw ValueError("valid range for emax is [0, MAX_EMAX]");
319     }
320   }
321 
emin(mpd_ssize_t v)322   ALWAYS_INLINE void emin(mpd_ssize_t v) {
323     if (!mpd_qsetemin(&ctx, v)) {
324       throw ValueError("valid range for emin is [MIN_EMIN, 0]");
325     }
326   }
327 
round(int v)328   ALWAYS_INLINE void round(int v) {
329     if (!mpd_qsetround(&ctx, v)) {
330       throw ValueError("valid values for rounding are:\n"
331                        "  [ROUND_CEILING, ROUND_FLOOR, ROUND_UP, ROUND_DOWN,\n"
332                        "   ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN,\n"
333                        "   ROUND_05UP]");
334     }
335   }
336 
status(uint32_t v)337   ALWAYS_INLINE void status(uint32_t v) {
338     if (!mpd_qsetstatus(&ctx, v)) {
339       throw ValueError("invalid status flag");
340     }
341   }
342 
traps(uint32_t v)343   ALWAYS_INLINE void traps(uint32_t v) {
344     if (!mpd_qsettraps(&ctx, v)) {
345       throw ValueError("invalid status flag");
346     }
347   }
348 
clamp(int v)349   ALWAYS_INLINE void clamp(int v) {
350     if (!mpd_qsetclamp(&ctx, v)) {
351       throw ValueError("invalid value for clamp");
352     }
353   }
354 
allcr(int v)355   ALWAYS_INLINE void allcr(int v) {
356     if (!mpd_qsetcr(&ctx, v)) {
357       throw ValueError("invalid value for allcr");
358     }
359   }
360 
361   /***********************************************************************/
362   /*                     Status and exception handling                   */
363   /***********************************************************************/
364   /* Add flags to status and raise an exception if a relevant trap is active */
raise(uint32_t flags)365   ALWAYS_INLINE void raise(uint32_t flags) {
366     ctx.status |= (flags & ~MPD_Malloc_error);
367     const uint32_t active_traps = flags & (ctx.traps|MPD_Malloc_error);
368     if (active_traps) {
369       raiseit(active_traps);
370     }
371   }
372 
373   /* Same, but with cleanup for constructors */
raise(uint32_t flags,mpd_t * a,bool isstatic)374   ALWAYS_INLINE void raise(uint32_t flags, mpd_t *a, bool isstatic) {
375     ctx.status |= (flags & ~MPD_Malloc_error);
376     const uint32_t active_traps = flags & (ctx.traps|MPD_Malloc_error);
377     if (active_traps) {
378       if (!isstatic) {
379         mpd_del(a);
380       }
381       raiseit(active_traps);
382     }
383   }
384 
385   /* Add selected status flags */
add_status(uint32_t flags)386   ALWAYS_INLINE void add_status(uint32_t flags) {
387     if (flags > MPD_Max_status) {
388       throw ValueError("invalid flags");
389     }
390     ctx.status |= flags;
391   }
392 
393   /* Clear all status flags */
clear_status()394   ALWAYS_INLINE void clear_status() {
395     ctx.status = 0;
396   }
397 
398   /* Clear selected status flags */
clear_status(uint32_t flags)399   ALWAYS_INLINE void clear_status(uint32_t flags) {
400     if (flags > MPD_Max_status) {
401       throw ValueError("invalid flags");
402     }
403     ctx.status &= ~flags;
404   }
405 
406   /* Add selected traps */
add_traps(uint32_t flags)407   ALWAYS_INLINE void add_traps(uint32_t flags) {
408     if (flags > MPD_Max_status) {
409       throw ValueError("invalid flags");
410     }
411     ctx.traps |= flags;
412   }
413 
414   /* Clear all traps */
clear_traps()415   ALWAYS_INLINE void clear_traps() {
416     ctx.traps = 0;
417   }
418 
419   /* Clear selected traps */
clear_traps(uint32_t flags)420   ALWAYS_INLINE void clear_traps(uint32_t flags) {
421     if (flags > MPD_Max_status) {
422       throw ValueError("invalid flags");
423     }
424     ctx.traps &= ~flags;
425   }
426 
427   /***********************************************************************/
428   /*                          String conversion                          */
429   /***********************************************************************/
430   IMPORTEXPORT std::string repr() const;
431   IMPORTEXPORT friend std::ostream& operator<<(std::ostream& os, const Context& c);
432 };
433 
434 IMPORTEXPORT Context MaxContext();
435 IMPORTEXPORT Context IEEEContext(int bits);
436 
437 
438 /******************************************************************************/
439 /*                                    Util                                    */
440 /******************************************************************************/
441 
442 namespace util {
443 
444 template <typename dest_t, typename src_t>
445 inline dest_t
safe_downcast(src_t v)446 safe_downcast(src_t v) {
447   if (v < std::numeric_limits<dest_t>::min() ||
448       v > std::numeric_limits<dest_t>::max()) {
449     throw ValueError("cast changes the original value");
450   }
451 
452   return static_cast<dest_t>(v);
453 }
454 
455 inline std::shared_ptr<const char>
shared_cp(const char * cp)456 shared_cp(const char *cp) {
457   if (cp == nullptr) {
458     throw RuntimeError("util::shared_cp: invalid nullptr argument");
459   }
460 
461   return std::shared_ptr<const char>(cp, [](const char *s){ mpd_free(const_cast<char *>(s)); });
462 }
463 
464 inline std::string
string_from_cp(const char * cp)465 string_from_cp(const char *cp) {
466   const auto p = shared_cp(cp);
467   return std::string(p.get());
468 }
469 
470 template <typename T>
471 struct int64_compat {
472 #define INT64_SUBSET(T) \
473   (INT64_MIN <= std::numeric_limits<T>::min() && std::numeric_limits<T>::max() <= INT64_MAX)
474 
475   static const bool value = std::is_same<T, int8_t>::value ||
476                             std::is_same<T, int16_t>::value ||
477                             std::is_same<T, int32_t>::value ||
478                             std::is_same<T, int64_t>::value ||
479                             (std::is_same<T, signed char>::value && INT64_SUBSET(signed char)) ||
480                             (std::is_same<T, short>::value && INT64_SUBSET(short)) ||
481                             (std::is_same<T, int>::value && INT64_SUBSET(int)) ||
482                             (std::is_same<T, long>::value && INT64_SUBSET(long)) ||
483                             (std::is_same<T, long long>::value && INT64_SUBSET(long long));
484 };
485 
486 template <typename T>
487 struct uint64_compat {
488 #define UINT64_SUBSET(T) (std::numeric_limits<T>::max() <= UINT64_MAX)
489 
490   static const bool value = std::is_same<T, uint8_t>::value ||
491                             std::is_same<T, uint16_t>::value ||
492                             std::is_same<T, uint32_t>::value ||
493                             std::is_same<T, uint64_t>::value ||
494                             (std::is_same<T, unsigned char>::value && UINT64_SUBSET(unsigned char)) ||
495                             (std::is_same<T, unsigned short>::value && UINT64_SUBSET(unsigned short)) ||
496                             (std::is_same<T, unsigned int>::value && UINT64_SUBSET(unsigned int)) ||
497                             (std::is_same<T, unsigned long>::value && UINT64_SUBSET(unsigned long)) ||
498                             (std::is_same<T, unsigned long long>::value && UINT64_SUBSET(unsigned long long));
499 };
500 
501 #define ENABLE_IF_SIGNED(T) \
502   template<typename T,      \
503            typename = typename std::enable_if<util::int64_compat<T>::value>::type>
504 
505 #define ENABLE_IF_UNSIGNED(T) \
506   template<typename T,        \
507            typename = typename std::enable_if<util::uint64_compat<T>::value>::type, typename = void>
508 
509 #define ENABLE_IF_INTEGRAL(T) \
510   template<typename T,        \
511            typename = typename std::enable_if<util::int64_compat<T>::value || util::uint64_compat<T>::value>::type>
512 
513 #define ASSERT_SIGNED(T) \
514   static_assert(util::int64_compat<T>::value, \
515                "internal error: selected type is not int64 compatible")
516 
517 #define ASSERT_UNSIGNED(T) \
518   static_assert(util::uint64_compat<T>::value, \
519                "internal error: selected type is not uint64 compatible")
520 
521 #define ASSERT_INTEGRAL(T) \
522   static_assert(util::int64_compat<T>::value || util::uint64_compat<T>::value, \
523                "internal error: selected type is not a suitable integer type")
524 }  // namespace util
525 
526 
527 /******************************************************************************/
528 /*                              Decimal object                                */
529 /******************************************************************************/
530 
531 constexpr mpd_ssize_t MINALLOC = 4;
532 
533 class Decimal {
534  private:
535   mpd_uint_t data[MINALLOC] = {0};
536 
537   mpd_t value {
538     MPD_STATIC|MPD_STATIC_DATA|MPD_SNAN, /* flags */
539     0,                                   /* exp */
540     0,                                   /* digits */
541     0,                                   /* len */
542     MINALLOC,                            /* alloc */
543     data                                 /* data */
544   };
545 
546   /* mpd_t accessors */
isstatic() const547   ALWAYS_INLINE bool isstatic() const { return value.data == data; }
548 
549   /* Reset rhs to snan after moving data to lhs */
reset()550   ALWAYS_INLINE void reset() {
551     value = {
552       MPD_STATIC|MPD_STATIC_DATA|MPD_SNAN, /* flags */
553       0,                                   /* exp */
554       0,                                   /* digits */
555       0,                                   /* len */
556       MINALLOC,                            /* alloc */
557       data                                 /* data */
558     };
559   }
560 
561   /* Copy flags, preserving memory attributes of result */
562   ALWAYS_INLINE uint8_t
copy_flags(const uint8_t rflags,const uint8_t aflags)563   copy_flags(const uint8_t rflags, const uint8_t aflags) {
564       return (rflags & (MPD_STATIC|MPD_DATAFLAGS)) |
565              (aflags & ~(MPD_STATIC|MPD_DATAFLAGS));
566   }
567 
568   ALWAYS_INLINE void
copy_value(const mpd_t * const src,const bool fastcopy)569   copy_value(const mpd_t *const src, const bool fastcopy) {
570     assert(mpd_isstatic(&value));
571     assert(mpd_isstatic(src));
572     assert(value.alloc >= MINALLOC);
573     assert(src->alloc >= MINALLOC);
574     assert(MPD_MINALLOC == MINALLOC);
575     if (fastcopy) {
576       value.data[0] = src->data[0];
577       value.data[1] = src->data[1];
578       value.data[2] = src->data[2];
579       value.data[3] = src->data[3];
580       value.flags = copy_flags(value.flags, src->flags);
581       value.exp = src->exp;
582       value.digits = src->digits;
583       value.len = src->len;
584     } else {
585       if (!mpd_qcopy_cxx(&value, src)) {
586         context.raise(MPD_Malloc_error);
587       }
588     }
589   }
590 
591   ALWAYS_INLINE void
move_value(const mpd_t * const src,const bool fastcopy)592   move_value(const mpd_t *const src, const bool fastcopy) {
593     assert(mpd_isstatic(&value));
594     assert(mpd_isstatic(src));
595     assert(value.alloc >= MINALLOC);
596     assert(src->alloc >= MINALLOC);
597     assert(MPD_MINALLOC == MINALLOC);
598     if (fastcopy) {
599       value.data[0] = src->data[0];
600       value.data[1] = src->data[1];
601       value.data[2] = src->data[2];
602       value.data[3] = src->data[3];
603       value.flags = copy_flags(value.flags, src->flags);
604       value.exp = src->exp;
605       value.digits = src->digits;
606       value.len = src->len;
607     }
608     else {
609       assert(mpd_isdynamic_data(src));
610       if (mpd_isdynamic_data(&value)) {
611         mpd_free(value.data);
612       }
613       value = *src;
614       assert(mpd_isstatic(&value));
615       assert(mpd_isdynamic_data(&value));
616     }
617   }
618 
unary_func_status(int (* func)(mpd_t *,const mpd_t *,uint32_t *)) const619   ALWAYS_INLINE Decimal unary_func_status(
620     int (* func)(mpd_t *, const mpd_t *, uint32_t *)) const {
621       Decimal result;
622       uint32_t status = 0;
623       if (!func(result.get(), getconst(), &status)) {
624           throw MallocError("out of memory");
625       }
626       return result;
627   }
628 
unary_func(void (* func)(mpd_t *,const mpd_t *,const mpd_context_t *,uint32_t *),Context & c=context) const629   ALWAYS_INLINE Decimal unary_func(
630     void (* func)(mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
631     Context& c=context) const {
632       Decimal result;
633       uint32_t status = 0;
634       func(result.get(), getconst(), c.getconst(), &status);
635       c.raise(status);
636       return result;
637   }
638 
binary_func_noctx(int (* func)(mpd_t *,const mpd_t *,const mpd_t *),const Decimal & other) const639   ALWAYS_INLINE Decimal binary_func_noctx(
640     int (* func)(mpd_t *, const mpd_t *, const mpd_t *),
641     const Decimal& other) const {
642       Decimal result;
643       (void)func(result.get(), getconst(), other.getconst());
644       return result;
645   }
646 
int_binary_func(int (* func)(mpd_t *,const mpd_t *,const mpd_t *,const mpd_context_t *,uint32_t *),const Decimal & other,Context & c=context) const647   ALWAYS_INLINE Decimal int_binary_func(
648     int (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
649     const Decimal& other,
650     Context& c=context) const {
651       Decimal result;
652       uint32_t status = 0;
653       (void)func(result.get(), getconst(), other.getconst(), c.getconst(), &status);
654       c.raise(status);
655       return result;
656   }
657 
binary_func(void (* func)(mpd_t *,const mpd_t *,const mpd_t *,const mpd_context_t *,uint32_t *),const Decimal & other,Context & c=context) const658   ALWAYS_INLINE Decimal binary_func(
659     void (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
660     const Decimal& other,
661     Context& c=context) const {
662       Decimal result;
663       uint32_t status = 0;
664       func(result.get(), getconst(), other.getconst(), c.getconst(), &status);
665       c.raise(status);
666       return result;
667   }
668 
inplace_binary_func(void (* func)(mpd_t *,const mpd_t *,const mpd_t *,const mpd_context_t *,uint32_t *),const Decimal & other,Context & c=context)669   ALWAYS_INLINE Decimal& inplace_binary_func(
670     void (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
671     const Decimal& other,
672     Context& c=context) {
673       uint32_t status = 0;
674       func(get(), getconst(), other.getconst(), c.getconst(), &status);
675       c.raise(status);
676       return *this;
677   }
678 
inplace_shiftl(const int64_t n,Context & c=context)679   ALWAYS_INLINE Decimal inplace_shiftl(const int64_t n, Context& c=context) {
680     uint32_t status = 0;
681     mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
682     mpd_qshiftl(get(), getconst(), nn, &status);
683     c.raise(status);
684     return *this;
685   }
686 
inplace_shiftr(const int64_t n,Context & c=context)687   ALWAYS_INLINE Decimal inplace_shiftr(const int64_t n, Context& c=context) {
688     uint32_t status = 0;
689     mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
690     mpd_qshiftr(get(), getconst(), nn, &status);
691     c.raise(status);
692     return *this;
693   }
694 
695  public:
696   /***********************************************************************/
697   /*               Exact conversions regardless of context               */
698   /***********************************************************************/
699 
700   /* Implicit */
701   Decimal() noexcept = default;
Decimal(const Decimal & other)702   Decimal(const Decimal& other) { *this = other; }
Decimal(Decimal && other)703   Decimal(Decimal&& other) noexcept { *this = std::move(other); }
704 
705   ENABLE_IF_SIGNED(T)
Decimal(const T & other)706   Decimal(const T& other) {
707     ASSERT_SIGNED(T);
708     uint32_t status = 0;
709     mpd_qset_i64_exact(&value, other, &status);
710     context.raise(status, &value, isstatic());
711   }
712 
713   ENABLE_IF_UNSIGNED(T)
Decimal(const T & other)714   Decimal(const T& other) {
715     ASSERT_UNSIGNED(T);
716     uint32_t status = 0;
717     mpd_qset_u64_exact(&value, other, &status);
718     context.raise(status, &value, isstatic());
719   }
720 
721   /* Explicit */
Decimal(const char * const s)722   explicit Decimal(const char * const s) {
723     uint32_t status = 0;
724     if (s == nullptr) {
725       throw ValueError("Decimal: string argument in constructor is NULL");
726     }
727     mpd_qset_string_exact(&value, s, &status);
728     context.raise(status, &value, isstatic());
729   }
730 
Decimal(const std::string & s)731   explicit Decimal(const std::string& s) {
732     uint32_t status = 0;
733     mpd_qset_string_exact(&value, s.c_str(), &status);
734     context.raise(status, &value, isstatic());
735   }
736 
Decimal(const mpd_uint128_triple_t & triple)737   explicit Decimal(const mpd_uint128_triple_t& triple) {
738     uint32_t status = 0;
739     if (mpd_from_uint128_triple(&value, &triple, &status) < 0) {
740       context.raise(status, &value, isstatic());
741     }
742   }
743 
744   /***********************************************************************/
745   /*                Inexact conversions that use a context               */
746   /***********************************************************************/
Decimal(const Decimal & other,Context & c)747   explicit Decimal(const Decimal& other, Context& c) {
748     const mpd_context_t *ctx = c.getconst();
749 
750     *this = other;
751 
752     if (mpd_isnan(&value) && value.digits > ctx->prec - ctx->clamp) {
753       /* Special case:  too many NaN payload digits */
754       mpd_setspecial(&value, MPD_POS, MPD_NAN);
755       c.raise(MPD_Conversion_syntax, &value, isstatic());
756     }
757     else {
758       uint32_t status = 0;
759       mpd_qfinalize(&value, ctx, &status);
760       c.raise(status, &value, isstatic());
761     }
762   }
763 
764   ENABLE_IF_SIGNED(T)
Decimal(const T & other,Context & c)765   explicit Decimal(const T& other, Context& c) {
766     ASSERT_SIGNED(T);
767     uint32_t status = 0;
768     mpd_qset_i64(&value, other, c.getconst(), &status);
769     c.raise(status, &value, isstatic());
770   }
771 
772   ENABLE_IF_UNSIGNED(T)
Decimal(const T & other,Context & c)773   explicit Decimal(const T& other, Context& c) {
774     ASSERT_UNSIGNED(T);
775     uint32_t status = 0;
776     mpd_qset_u64(&value, other, c.getconst(), &status);
777     c.raise(status, &value, isstatic());
778   }
779 
Decimal(const char * const s,Context & c)780   explicit Decimal(const char * const s, Context& c) {
781     uint32_t status = 0;
782     if (s == nullptr) {
783       throw ValueError("Decimal: string argument in constructor is NULL");
784     }
785     mpd_qset_string(&value, s, c.getconst(), &status);
786     c.raise(status, &value, isstatic());
787   }
788 
Decimal(const std::string & s,Context & c)789   explicit Decimal(const std::string& s, Context& c) {
790     uint32_t status = 0;
791     mpd_qset_string(&value, s.c_str(), c.getconst(), &status);
792     c.raise(status, &value, isstatic());
793   }
794 
795   /***********************************************************************/
796   /*                              Accessors                              */
797   /***********************************************************************/
get()798   ALWAYS_INLINE mpd_t *get() { return &value; }
getconst() const799   ALWAYS_INLINE const mpd_t *getconst() const { return &value; }
sign() const800   ALWAYS_INLINE int sign() const { return mpd_isnegative(&value) ? -1 : 1; }
801 
coeff() const802   ALWAYS_INLINE Decimal coeff() const {
803     if (isspecial()) {
804       throw ValueError("coefficient is undefined for special values");
805     }
806 
807     Decimal result = *this;
808     mpd_set_positive(&result.value);
809     result.value.exp = 0;
810     return result;
811   }
812 
exponent() const813   ALWAYS_INLINE int64_t exponent() const {
814     if (isspecial()) {
815       throw ValueError("exponent is undefined for special values");
816     }
817 
818     return value.exp;
819   }
820 
payload() const821   ALWAYS_INLINE Decimal payload() const {
822     if (!isnan()) {
823       throw ValueError("payload is only defined for NaNs");
824     }
825     if (value.len == 0) {
826       return Decimal(0);
827     }
828 
829     Decimal result = *this;
830     mpd_set_flags(&result.value, 0);
831     result.value.exp = 0;
832     return result;
833   }
834 
835   /***********************************************************************/
836   /*                             Destructor                              */
837   /***********************************************************************/
~Decimal()838   ~Decimal() { if (value.data != data) mpd_del(&value); }
839 
840   /***********************************************************************/
841   /*                         Assignment operators                        */
842   /***********************************************************************/
operator =(const Decimal & other)843   ALWAYS_INLINE Decimal& operator= (const Decimal& other) {
844     copy_value(other.getconst(), other.isstatic());
845     return *this;
846   }
847 
operator =(Decimal && other)848   ALWAYS_INLINE Decimal& operator= (Decimal&& other) noexcept {
849     if (this != &other) {
850       move_value(other.getconst(), other.isstatic());
851       other.reset();
852     }
853     return *this;
854   }
855 
operator +=(const Decimal & other)856   ALWAYS_INLINE Decimal& operator+= (const Decimal& other) { return inplace_binary_func(mpd_qadd, other); }
operator -=(const Decimal & other)857   ALWAYS_INLINE Decimal& operator-= (const Decimal& other) { return inplace_binary_func(mpd_qsub, other); }
operator *=(const Decimal & other)858   ALWAYS_INLINE Decimal& operator*= (const Decimal& other) { return inplace_binary_func(mpd_qmul, other); }
operator /=(const Decimal & other)859   ALWAYS_INLINE Decimal& operator/= (const Decimal& other) { return inplace_binary_func(mpd_qdiv, other); }
operator %=(const Decimal & other)860   ALWAYS_INLINE Decimal& operator%= (const Decimal& other) { return inplace_binary_func(mpd_qrem, other); }
861 
862   /***********************************************************************/
863   /*                         Comparison operators                        */
864   /***********************************************************************/
operator ==(const Decimal & other) const865   ALWAYS_INLINE bool operator== (const Decimal& other) const {
866     uint32_t status = 0;
867     const int r = mpd_qcmp(getconst(), other.getconst(), &status);
868     if (r == INT_MAX) {
869       if (issnan() || other.issnan()) {
870         context.raise(status);
871       }
872       return false;
873     }
874     return r == 0;
875   }
876 
operator !=(const Decimal & other) const877   ALWAYS_INLINE bool operator!= (const Decimal& other) const {
878     uint32_t status = 0;
879     const int r = mpd_qcmp(getconst(), other.getconst(), &status);
880     if (r == INT_MAX) {
881       if (issnan() || other.issnan()) {
882           context.raise(status);
883       }
884       return true;
885     }
886     return r != 0;
887   }
888 
operator <(const Decimal & other) const889   ALWAYS_INLINE bool operator< (const Decimal& other) const {
890     uint32_t status = 0;
891     const int r = mpd_qcmp(getconst(), other.getconst(), &status);
892     if (r == INT_MAX) {
893       context.raise(status);
894       return false;
895     }
896     return r < 0;
897   }
898 
operator <=(const Decimal & other) const899   ALWAYS_INLINE bool operator<= (const Decimal& other) const {
900     uint32_t status = 0;
901     const int r = mpd_qcmp(getconst(), other.getconst(), &status);
902     if (r == INT_MAX) {
903       context.raise(status);
904       return false;
905     }
906     return r <= 0;
907   }
908 
operator >=(const Decimal & other) const909   ALWAYS_INLINE bool operator>= (const Decimal& other) const {
910     uint32_t status = 0;
911     const int r = mpd_qcmp(getconst(), other.getconst(), &status);
912     if (r == INT_MAX) {
913       context.raise(status);
914       return false;
915     }
916     return r >= 0;
917   }
918 
operator >(const Decimal & other) const919   ALWAYS_INLINE bool operator> (const Decimal& other) const {
920     uint32_t status = 0;
921     const int r = mpd_qcmp(getconst(), other.getconst(), &status);
922     if (r == INT_MAX) {
923       context.raise(status);
924       return false;
925     }
926     return r > 0;
927   }
928 
929   /***********************************************************************/
930   /*                      Unary arithmetic operators                     */
931   /***********************************************************************/
operator -() const932   ALWAYS_INLINE Decimal operator- () const { return unary_func(mpd_qminus); }
operator +() const933   ALWAYS_INLINE Decimal operator+ () const { return unary_func(mpd_qplus); }
934 
935   /***********************************************************************/
936   /*                      Binary arithmetic operators                    */
937   /***********************************************************************/
operator +(const Decimal & other) const938   ALWAYS_INLINE Decimal operator+ (const Decimal& other) const { return binary_func(mpd_qadd, other); }
operator -(const Decimal & other) const939   ALWAYS_INLINE Decimal operator- (const Decimal& other) const { return binary_func(mpd_qsub, other); }
operator *(const Decimal & other) const940   ALWAYS_INLINE Decimal operator* (const Decimal& other) const { return binary_func(mpd_qmul, other); }
operator /(const Decimal & other) const941   ALWAYS_INLINE Decimal operator/ (const Decimal& other) const { return binary_func(mpd_qdiv, other); }
operator %(const Decimal & other) const942   ALWAYS_INLINE Decimal operator% (const Decimal& other) const { return binary_func(mpd_qrem, other); }
943 
944   /***********************************************************************/
945   /*                             Predicates                              */
946   /***********************************************************************/
947   /* Predicates, no context arg */
iscanonical() const948   ALWAYS_INLINE bool iscanonical() const { return mpd_iscanonical(getconst()); }
isfinite() const949   ALWAYS_INLINE bool isfinite() const { return mpd_isfinite(getconst()); }
isinfinite() const950   ALWAYS_INLINE bool isinfinite() const { return mpd_isinfinite(getconst()); }
isspecial() const951   ALWAYS_INLINE bool isspecial() const { return mpd_isspecial(getconst()); }
isnan() const952   ALWAYS_INLINE bool isnan() const { return mpd_isnan(getconst()); }
isqnan() const953   ALWAYS_INLINE bool isqnan() const { return mpd_isqnan(getconst()); }
issnan() const954   ALWAYS_INLINE bool issnan() const { return mpd_issnan(getconst()); }
issigned() const955   ALWAYS_INLINE bool issigned() const { return mpd_issigned(getconst()); }
iszero() const956   ALWAYS_INLINE bool iszero() const { return mpd_iszero(getconst()); }
isinteger() const957   ALWAYS_INLINE bool isinteger() const { return mpd_isinteger(getconst()); }
958 
959   /* Predicates, optional context arg */
isnormal(const Context & c=context) const960   ALWAYS_INLINE bool isnormal(const Context& c=context) const { return mpd_isnormal(getconst(), c.getconst()); }
issubnormal(const Context & c=context) const961   ALWAYS_INLINE bool issubnormal(const Context& c=context) const { return mpd_issubnormal(getconst(), c.getconst()); }
962 
963   /***********************************************************************/
964   /*                           Unary functions                           */
965   /***********************************************************************/
966   /* Unary functions, no context arg */
adjexp() const967   ALWAYS_INLINE int64_t adjexp() const {
968     if (isspecial()) {
969       throw ValueError("adjusted exponent undefined for special values");
970     }
971     return mpd_adjexp(getconst());
972   }
973 
canonical() const974   ALWAYS_INLINE Decimal canonical() const { return *this; }
copy() const975   ALWAYS_INLINE Decimal copy() const { return unary_func_status(mpd_qcopy); }
copy_abs() const976   ALWAYS_INLINE Decimal copy_abs() const { return unary_func_status(mpd_qcopy_abs); }
copy_negate() const977   ALWAYS_INLINE Decimal copy_negate() const { return unary_func_status(mpd_qcopy_negate); }
978 
979   /* Unary functions, optional context arg */
number_class(Context & c=context) const980   ALWAYS_INLINE std::string number_class(Context& c=context) const { return mpd_class(getconst(), c.getconst()); }
981 
abs(Context & c=context) const982   ALWAYS_INLINE Decimal abs(Context& c=context) const { return unary_func(mpd_qabs, c); }
ceil(Context & c=context) const983   ALWAYS_INLINE Decimal ceil(Context& c=context) const { return unary_func(mpd_qceil, c); }
exp(Context & c=context) const984   ALWAYS_INLINE Decimal exp(Context& c=context) const { return unary_func(mpd_qexp, c); }
floor(Context & c=context) const985   ALWAYS_INLINE Decimal floor(Context& c=context) const { return unary_func(mpd_qfloor, c); }
invroot(Context & c=context) const986   ALWAYS_INLINE Decimal invroot(Context& c=context) const { return unary_func(mpd_qinvroot, c); }
logical_invert(Context & c=context) const987   ALWAYS_INLINE Decimal logical_invert(Context& c=context) const { return unary_func(mpd_qinvert, c); }
ln(Context & c=context) const988   ALWAYS_INLINE Decimal ln(Context& c=context) const { return unary_func(mpd_qln, c); }
log10(Context & c=context) const989   ALWAYS_INLINE Decimal log10(Context& c=context) const { return unary_func(mpd_qlog10, c); }
logb(Context & c=context) const990   ALWAYS_INLINE Decimal logb(Context& c=context) const { return unary_func(mpd_qlogb, c); }
minus(Context & c=context) const991   ALWAYS_INLINE Decimal minus(Context& c=context) const { return unary_func(mpd_qminus, c); }
next_minus(Context & c=context) const992   ALWAYS_INLINE Decimal next_minus(Context& c=context) const { return unary_func(mpd_qnext_minus, c); }
next_plus(Context & c=context) const993   ALWAYS_INLINE Decimal next_plus(Context& c=context) const { return unary_func(mpd_qnext_plus, c); }
plus(Context & c=context) const994   ALWAYS_INLINE Decimal plus(Context& c=context) const { return unary_func(mpd_qplus, c); }
reduce(Context & c=context) const995   ALWAYS_INLINE Decimal reduce(Context& c=context) const { return unary_func(mpd_qreduce, c); }
to_integral(Context & c=context) const996   ALWAYS_INLINE Decimal to_integral(Context& c=context) const { return unary_func(mpd_qround_to_int, c); }
to_integral_exact(Context & c=context) const997   ALWAYS_INLINE Decimal to_integral_exact(Context& c=context) const { return unary_func(mpd_qround_to_intx, c); }
sqrt(Context & c=context) const998   ALWAYS_INLINE Decimal sqrt(Context& c=context) const { return unary_func(mpd_qsqrt, c); }
trunc(Context & c=context) const999   ALWAYS_INLINE Decimal trunc(Context& c=context) const { return unary_func(mpd_qtrunc, c); }
1000 
1001   /***********************************************************************/
1002   /*                           Binary functions                          */
1003   /***********************************************************************/
1004   /* Binary functions, no context arg */
compare_total(const Decimal & other) const1005   ALWAYS_INLINE Decimal compare_total(const Decimal& other) const { return binary_func_noctx(mpd_compare_total, other); }
compare_total_mag(const Decimal & other) const1006   ALWAYS_INLINE Decimal compare_total_mag(const Decimal& other) const { return binary_func_noctx(mpd_compare_total_mag, other); }
1007 
1008   /* Binary arithmetic functions, optional context arg */
add(const Decimal & other,Context & c=context) const1009   ALWAYS_INLINE Decimal add(const Decimal& other, Context& c=context) const { return binary_func(mpd_qadd, other, c); }
div(const Decimal & other,Context & c=context) const1010   ALWAYS_INLINE Decimal div(const Decimal& other, Context& c=context) const { return binary_func(mpd_qdiv, other, c); }
divint(const Decimal & other,Context & c=context) const1011   ALWAYS_INLINE Decimal divint(const Decimal& other, Context& c=context) const { return binary_func(mpd_qdivint, other, c); }
compare(const Decimal & other,Context & c=context) const1012   ALWAYS_INLINE Decimal compare(const Decimal& other, Context& c=context) const { return int_binary_func(mpd_qcompare, other, c); }
compare_signal(const Decimal & other,Context & c=context) const1013   ALWAYS_INLINE Decimal compare_signal(const Decimal& other, Context& c=context) const { return int_binary_func(mpd_qcompare_signal, other, c); }
logical_and(const Decimal & other,Context & c=context) const1014   ALWAYS_INLINE Decimal logical_and(const Decimal& other, Context& c=context) const { return binary_func(mpd_qand, other, c); }
logical_or(const Decimal & other,Context & c=context) const1015   ALWAYS_INLINE Decimal logical_or(const Decimal& other, Context& c=context) const { return binary_func(mpd_qor, other, c); }
logical_xor(const Decimal & other,Context & c=context) const1016   ALWAYS_INLINE Decimal logical_xor(const Decimal& other, Context& c=context) const { return binary_func(mpd_qxor, other, c); }
max(const Decimal & other,Context & c=context) const1017   ALWAYS_INLINE Decimal max(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmax, other, c); }
max_mag(const Decimal & other,Context & c=context) const1018   ALWAYS_INLINE Decimal max_mag(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmax_mag, other, c); }
min(const Decimal & other,Context & c=context) const1019   ALWAYS_INLINE Decimal min(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmin, other, c); }
min_mag(const Decimal & other,Context & c=context) const1020   ALWAYS_INLINE Decimal min_mag(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmin_mag, other, c); }
mul(const Decimal & other,Context & c=context) const1021   ALWAYS_INLINE Decimal mul(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmul, other, c); }
next_toward(const Decimal & other,Context & c=context) const1022   ALWAYS_INLINE Decimal next_toward(const Decimal& other, Context& c=context) const { return binary_func(mpd_qnext_toward, other, c); }
pow(const Decimal & other,Context & c=context) const1023   ALWAYS_INLINE Decimal pow(const Decimal& other, Context& c=context) const { return binary_func(mpd_qpow, other, c); }
quantize(const Decimal & other,Context & c=context) const1024   ALWAYS_INLINE Decimal quantize(const Decimal& other, Context& c=context) const { return binary_func(mpd_qquantize, other, c); }
rem(const Decimal & other,Context & c=context) const1025   ALWAYS_INLINE Decimal rem(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrem, other, c); }
rem_near(const Decimal & other,Context & c=context) const1026   ALWAYS_INLINE Decimal rem_near(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrem_near, other, c); }
rotate(const Decimal & other,Context & c=context) const1027   ALWAYS_INLINE Decimal rotate(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrotate, other, c); }
scaleb(const Decimal & other,Context & c=context) const1028   ALWAYS_INLINE Decimal scaleb(const Decimal& other, Context& c=context) const { return binary_func(mpd_qscaleb, other, c); }
shift(const Decimal & other,Context & c=context) const1029   ALWAYS_INLINE Decimal shift(const Decimal& other, Context& c=context) const { return binary_func(mpd_qshift, other, c); }
sub(const Decimal & other,Context & c=context) const1030   ALWAYS_INLINE Decimal sub(const Decimal& other, Context& c=context) const { return binary_func(mpd_qsub, other, c); }
1031 
1032   /* Binary arithmetic function, two return values */
divmod(const Decimal & other,Context & c=context) const1033   ALWAYS_INLINE std::pair<Decimal, Decimal> divmod(const Decimal& other, Context& c=context) const {
1034     std::pair<Decimal, Decimal> result;
1035     uint32_t status = 0;
1036     mpd_qdivmod(result.first.get(), result.second.get(), getconst(), other.getconst(), c.getconst(), &status);
1037     c.raise(status);
1038     return result;
1039   }
1040 
1041   /***********************************************************************/
1042   /*                          Ternary functions                          */
1043   /***********************************************************************/
fma(const Decimal & other,const Decimal & third,Context & c=context) const1044   ALWAYS_INLINE Decimal fma(const Decimal& other, const Decimal& third, Context& c=context) const {
1045     Decimal result;
1046     uint32_t status = 0;
1047     mpd_qfma(result.get(), getconst(), other.getconst(), third.getconst(), c.getconst(), &status);
1048     c.raise(status);
1049     return result;
1050   }
1051 
powmod(const Decimal & other,const Decimal & third,Context & c=context) const1052   ALWAYS_INLINE Decimal powmod(const Decimal& other, const Decimal& third, Context& c=context) const {
1053     Decimal result;
1054     uint32_t status = 0;
1055     mpd_qpowmod(result.get(), getconst(), other.getconst(), third.getconst(), c.getconst(), &status);
1056     c.raise(status);
1057     return result;
1058   }
1059 
1060   /***********************************************************************/
1061   /*                         Irregular functions                         */
1062   /***********************************************************************/
apply(Context & c=context) const1063   ALWAYS_INLINE Decimal apply(Context& c=context) const {
1064     Decimal result = *this;
1065     uint32_t status = 0;
1066 
1067     mpd_qfinalize(result.get(), c.getconst(), &status);
1068     c.raise(status);
1069     return result;
1070   }
1071 
cmp(const Decimal & other) const1072   ALWAYS_INLINE int cmp(const Decimal& other) const {
1073     uint32_t status = 0;
1074     return mpd_qcmp(getconst(), other.getconst(), &status);
1075   }
1076 
cmp_total(const Decimal & other) const1077   ALWAYS_INLINE int cmp_total(const Decimal& other) const {
1078     return mpd_cmp_total(getconst(), other.getconst());
1079   }
1080 
cmp_total_mag(const Decimal & other) const1081   ALWAYS_INLINE int cmp_total_mag(const Decimal& other) const {
1082     return mpd_cmp_total_mag(getconst(), other.getconst());
1083   }
1084 
copy_sign(const Decimal & other) const1085   ALWAYS_INLINE Decimal copy_sign(const Decimal& other) const {
1086     Decimal result;
1087     uint32_t status = 0;
1088     if (!mpd_qcopy_sign(result.get(), getconst(), other.getconst(), &status)) {
1089       throw MallocError("out of memory");
1090     }
1091     return result;
1092   }
1093 
rescale(const int64_t exp,Context & c=context) const1094   ALWAYS_INLINE Decimal rescale(const int64_t exp, Context& c=context) const {
1095     Decimal result;
1096     uint32_t status = 0;
1097     mpd_ssize_t xexp = util::safe_downcast<mpd_ssize_t, int64_t>(exp);
1098     mpd_qrescale(result.get(), getconst(), xexp, c.getconst(), &status);
1099     c.raise(status);
1100     return result;
1101   }
1102 
same_quantum(const Decimal & other) const1103   ALWAYS_INLINE bool same_quantum(const Decimal& other) const {
1104     return mpd_same_quantum(getconst(), other.getconst());
1105   }
1106 
shiftn(const int64_t n,Context & c=context) const1107   ALWAYS_INLINE Decimal shiftn(const int64_t n, Context& c=context) const {
1108     Decimal result;
1109     uint32_t status = 0;
1110     mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
1111     mpd_qshiftn(result.get(), getconst(), nn, c.getconst(), &status);
1112     c.raise(status);
1113     return result;
1114   }
1115 
shiftl(const int64_t n,Context & c=context) const1116   ALWAYS_INLINE Decimal shiftl(const int64_t n, Context& c=context) const {
1117     Decimal result;
1118     uint32_t status = 0;
1119     if (isspecial()) {
1120       throw ValueError("shiftl: cannot handle special numbers");
1121     }
1122     if (n < 0 || n > MPD_MAX_PREC - getconst()->digits) {
1123       throw ValueError("shiftl: shift is negative or too large");
1124     }
1125     mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
1126     mpd_qshiftl(result.get(), getconst(), nn, &status);
1127     c.raise(status);
1128     return result;
1129   }
1130 
shiftr(const int64_t n,Context & c=context) const1131   ALWAYS_INLINE Decimal shiftr(const int64_t n, Context& c=context) const {
1132     Decimal result;
1133     uint32_t status = 0;
1134     if (isspecial()) {
1135       throw ValueError("shiftr: cannot handle special numbers");
1136     }
1137     if (n < 0) {
1138       throw ValueError("shiftr: shift is negative");
1139     }
1140     mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
1141     mpd_qshiftr(result.get(), getconst(), nn, &status);
1142     c.raise(status);
1143     return result;
1144   }
1145 
1146   IMPORTEXPORT static Decimal exact(const char *s, Context& c);
1147   IMPORTEXPORT static Decimal exact(const std::string& s, Context& c);
1148   IMPORTEXPORT static Decimal ln10(int64_t n, Context& c=context);
1149   IMPORTEXPORT static int32_t radix();
1150 
1151   /***********************************************************************/
1152   /*                          Integer conversion                         */
1153   /***********************************************************************/
i64() const1154   ALWAYS_INLINE int64_t i64() const {
1155     uint32_t status = 0;
1156     const int64_t result = mpd_qget_i64(getconst(), &status);
1157     if (status) {
1158       throw ValueError("cannot convert to int64_t");
1159     }
1160     return result;
1161   }
1162 
i32() const1163   ALWAYS_INLINE int32_t i32() const {
1164     uint32_t status = 0;
1165     const int32_t result = mpd_qget_i32(getconst(), &status);
1166     if (status) {
1167       throw ValueError("cannot convert to int32_t");
1168     }
1169     return result;
1170   }
1171 
u64() const1172   ALWAYS_INLINE uint64_t u64() const {
1173     uint32_t status = 0;
1174     const uint64_t result = mpd_qget_u64(getconst(), &status);
1175     if (status) {
1176       throw ValueError("cannot convert to uint64_t");
1177     }
1178     return result;
1179   }
1180 
u32() const1181   ALWAYS_INLINE uint32_t u32() const {
1182     uint32_t status = 0;
1183     const uint32_t result = mpd_qget_u32(getconst(), &status);
1184     if (status) {
1185       throw ValueError("cannot convert to uint32_t");
1186     }
1187     return result;
1188   }
1189 
1190   /***********************************************************************/
1191   /*                           Triple conversion                         */
1192   /***********************************************************************/
as_uint128_triple() const1193   ALWAYS_INLINE mpd_uint128_triple_t as_uint128_triple() const {
1194     return mpd_as_uint128_triple(getconst());
1195   }
1196 
1197   /***********************************************************************/
1198   /*                          String conversion                          */
1199   /***********************************************************************/
1200   /* String representations */
1201   IMPORTEXPORT std::string repr(bool capitals=true) const;
1202 
to_sci(bool capitals=true) const1203   inline std::string to_sci(bool capitals=true) const {
1204     const char *cp = mpd_to_sci(getconst(), capitals);
1205     if (cp == nullptr) {
1206       throw MallocError("out of memory");
1207     }
1208 
1209     return util::string_from_cp(cp);
1210   }
1211 
to_eng(bool capitals=true) const1212   inline std::string to_eng(bool capitals=true) const {
1213     const char *cp = mpd_to_eng(getconst(), capitals);
1214     if (cp == nullptr) {
1215       throw MallocError("out of memory");
1216     }
1217 
1218     return util::string_from_cp(cp);
1219   }
1220 
format(const char * fmt,const Context & c=context) const1221   inline std::string format(const char *fmt, const Context& c=context) const {
1222     uint32_t status = 0;
1223     mpd_context_t ctx;
1224 
1225     if (fmt == nullptr) {
1226       throw ValueError("Decimal.format: fmt argument is NULL");
1227     }
1228 
1229     mpd_maxcontext(&ctx);
1230     ctx.round = c.getconst()->round;
1231     ctx.traps = 0;
1232 
1233     const char *cp = mpd_qformat(getconst(), fmt, &ctx, &status);
1234     if (cp == nullptr) {
1235       if (status & MPD_Malloc_error) {
1236         throw MallocError("out of memory");
1237       }
1238       else if (status & MPD_Invalid_operation) {
1239         throw ValueError("invalid format string");
1240       }
1241       else {
1242         throw RuntimeError("internal error: unexpected status");
1243       }
1244     }
1245 
1246     return util::string_from_cp(cp);
1247   }
1248 
format(const std::string & s,const Context & c=context) const1249   inline std::string format(const std::string& s, const Context& c=context) const {
1250     return format(s.c_str(), c);
1251   }
1252 
1253   IMPORTEXPORT friend std::ostream& operator<< (std::ostream& os, const Decimal& dec);
1254 };
1255 
1256 /***********************************************************************/
1257 /*                      Reverse comparison operators                   */
1258 /***********************************************************************/
1259 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator==(const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) == self; }
1260 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator!= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) != self; }
1261 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator< (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) < self; }
1262 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator<= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) <= self; }
ENABLE_IF_INTEGRAL(T)1263 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator>= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) >= self; }
ENABLE_IF_INTEGRAL(T)1264 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator> (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) > self; }
1265 
1266 /***********************************************************************/
1267 /*                      Reverse arithmetic operators                   */
1268 /***********************************************************************/
1269 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator+ (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) + self; }
1270 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator- (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) - self; }
ENABLE_IF_INTEGRAL(T)1271 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator* (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) * self; }
1272 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator/ (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) / self; }
1273 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator% (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) % self; }
1274 
1275 
1276 #undef IMPORTEXPORT
1277 #undef ALWAYS_INLINE
1278 #undef INT64_SUBSET
1279 #undef UINT64_SUBSET
1280 #undef ENABLE_IF_SIGNED
1281 #undef ENABLE_IF_UNSIGNED
1282 #undef ENABLE_IF_INTEGRAL
1283 #undef ASSERT_SIGNED
1284 #undef ASSERT_UNSIGNED
1285 #undef ASSERT_INTEGRAL
1286 }  // namespace decimal
1287 
1288 
1289 #endif  // LIBMPDECXX_DECIMAL_HH_
1290