exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

amount.c (21853B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2021 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file util/amount.c
     18  * @brief Common utility functions to deal with units of currency
     19  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
     20  * @author Florian Dold
     21  * @author Benedikt Mueller
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/platform.h"
     25 #include "taler/taler_util.h"
     26 
     27 
     28 /**
     29  * Set @a a to "invalid".
     30  *
     31  * @param[out] a amount to set to invalid
     32  */
     33 static void
     34 invalidate (struct TALER_Amount *a)
     35 {
     36   memset (a,
     37           0,
     38           sizeof (struct TALER_Amount));
     39 }
     40 
     41 
     42 enum GNUNET_GenericReturnValue
     43 TALER_check_currency (const char *str)
     44 {
     45   size_t len = strlen (str);
     46   if (len >= TALER_CURRENCY_LEN)
     47   {
     48     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     49                 "Currency code name `%s' is too long\n",
     50                 str);
     51     return GNUNET_SYSERR;
     52   }
     53   if (len == 0)
     54   {
     55     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     56                 "Currency code name must be set\n");
     57     return GNUNET_SYSERR;
     58   }
     59   /* validate str has only legal characters in it! */
     60   for (unsigned int i = 0; '\0' != str[i]; i++)
     61   {
     62     if ( ('A' > str[i]) || ('Z' < str[i]) )
     63     {
     64       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     65                   "Currency code name `%s' contains illegal characters (only A-Z allowed)\n",
     66                   str);
     67       return GNUNET_SYSERR;
     68     }
     69   }
     70   return GNUNET_OK;
     71 }
     72 
     73 
     74 enum GNUNET_GenericReturnValue
     75 TALER_string_to_amount (const char *str,
     76                         struct TALER_Amount *amount)
     77 {
     78   int n;
     79   uint32_t b;
     80   const char *colon;
     81   const char *value;
     82 
     83   /* skip leading whitespace */
     84   while (isspace ( (unsigned char) str[0]))
     85     str++;
     86   if ('\0' == str[0])
     87   {
     88     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     89                 "Null before currency\n");
     90     invalidate (amount);
     91     return GNUNET_SYSERR;
     92   }
     93 
     94   /* parse currency */
     95   colon = strchr (str, (int) ':');
     96   if ( (NULL == colon) ||
     97        (colon == str) ||
     98        ((colon - str) >= TALER_CURRENCY_LEN) )
     99   {
    100     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    101                 "Invalid currency specified before colon: `%s'\n",
    102                 str);
    103     invalidate (amount);
    104     return GNUNET_SYSERR;
    105   }
    106 
    107   GNUNET_assert (TALER_CURRENCY_LEN > (colon - str));
    108   for (unsigned int i = 0; i<colon - str; i++)
    109     amount->currency[i] = str[i];
    110   /* 0-terminate *and* normalize buffer by setting everything to '\0' */
    111   memset (&amount->currency [colon - str],
    112           0,
    113           TALER_CURRENCY_LEN - (colon - str));
    114   if (GNUNET_OK !=
    115       TALER_check_currency (amount->currency))
    116     return GNUNET_SYSERR;
    117   /* skip colon */
    118   value = colon + 1;
    119   if ('\0' == value[0])
    120   {
    121     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    122                 "Actual value missing in amount `%s'\n",
    123                 str);
    124     invalidate (amount);
    125     return GNUNET_SYSERR;
    126   }
    127 
    128   amount->value = 0;
    129   amount->fraction = 0;
    130 
    131   /* parse value */
    132   while ('.' != *value)
    133   {
    134     if ('\0' == *value)
    135     {
    136       /* we are done */
    137       return GNUNET_OK;
    138     }
    139     if ( (*value < '0') ||
    140          (*value > '9') )
    141     {
    142       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    143                   "Invalid character `%c' in amount `%s'\n",
    144                   (int) *value,
    145                   str);
    146       invalidate (amount);
    147       return GNUNET_SYSERR;
    148     }
    149     n = *value - '0';
    150     if ( (amount->value * 10 < amount->value) ||
    151          (amount->value * 10 + n < amount->value) ||
    152          (amount->value > TALER_AMOUNT_MAX_VALUE) ||
    153          (amount->value * 10 + n > TALER_AMOUNT_MAX_VALUE) )
    154     {
    155       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    156                   "Value specified in amount `%s' is too large\n",
    157                   str);
    158       invalidate (amount);
    159       return GNUNET_SYSERR;
    160     }
    161     amount->value = (amount->value * 10) + n;
    162     value++;
    163   }
    164 
    165   /* skip the dot */
    166   value++;
    167 
    168   /* parse fraction */
    169   if ('\0' == *value)
    170   {
    171     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    172                 "Amount `%s' ends abruptly after `.'\n",
    173                 str);
    174     invalidate (amount);
    175     return GNUNET_SYSERR;
    176   }
    177   b = TALER_AMOUNT_FRAC_BASE / 10;
    178   while ('\0' != *value)
    179   {
    180     if (0 == b)
    181     {
    182       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    183                   "Fractional value too small (only %u digits supported) in amount `%s'\n",
    184                   (unsigned int) TALER_AMOUNT_FRAC_LEN,
    185                   str);
    186       invalidate (amount);
    187       return GNUNET_SYSERR;
    188     }
    189     if ( (*value < '0') ||
    190          (*value > '9') )
    191     {
    192       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    193                   "Error after dot\n");
    194       invalidate (amount);
    195       return GNUNET_SYSERR;
    196     }
    197     n = *value - '0';
    198     amount->fraction += n * b;
    199     b /= 10;
    200     value++;
    201   }
    202   return GNUNET_OK;
    203 }
    204 
    205 
    206 enum GNUNET_GenericReturnValue
    207 TALER_string_to_amount_nbo (const char *str,
    208                             struct TALER_AmountNBO *amount_nbo)
    209 {
    210   struct TALER_Amount amount;
    211 
    212   if (GNUNET_OK !=
    213       TALER_string_to_amount (str,
    214                               &amount))
    215     return GNUNET_SYSERR;
    216   TALER_amount_hton (amount_nbo,
    217                      &amount);
    218   return GNUNET_OK;
    219 }
    220 
    221 
    222 void
    223 TALER_amount_hton (struct TALER_AmountNBO *res,
    224                    const struct TALER_Amount *d)
    225 {
    226   GNUNET_assert (GNUNET_YES ==
    227                  TALER_amount_is_valid (d));
    228   res->value = GNUNET_htonll (d->value);
    229   res->fraction = htonl (d->fraction);
    230   for (unsigned int i = 0; i<TALER_CURRENCY_LEN; i++)
    231     res->currency[i] = d->currency[i];
    232 }
    233 
    234 
    235 void
    236 TALER_amount_ntoh (struct TALER_Amount *res,
    237                    const struct TALER_AmountNBO *dn)
    238 {
    239   res->value = GNUNET_ntohll (dn->value);
    240   res->fraction = ntohl (dn->fraction);
    241   GNUNET_memcpy (res->currency,
    242                  dn->currency,
    243                  TALER_CURRENCY_LEN);
    244   GNUNET_assert (GNUNET_YES ==
    245                  TALER_amount_is_valid (res));
    246 }
    247 
    248 
    249 enum GNUNET_GenericReturnValue
    250 TALER_amount_set_zero (const char *cur,
    251                        struct TALER_Amount *amount)
    252 {
    253   char tmp[TALER_CURRENCY_LEN];
    254   size_t slen;
    255 
    256   if (GNUNET_OK !=
    257       TALER_check_currency (cur))
    258     return GNUNET_SYSERR;
    259   slen = strlen (cur);
    260   /* make a copy of 'cur' to 'tmp' as the memset may clobber cur
    261      if cur aliases &amount->currency! */
    262   memcpy (tmp,
    263           cur,
    264           slen);
    265   memset (amount,
    266           0,
    267           sizeof (struct TALER_Amount));
    268   for (unsigned int i = 0; i<slen; i++)
    269     amount->currency[i] = tmp[i];
    270   return GNUNET_OK;
    271 }
    272 
    273 
    274 enum GNUNET_GenericReturnValue
    275 TALER_amount_is_valid (const struct TALER_Amount *amount)
    276 {
    277   if (amount->value > TALER_AMOUNT_MAX_VALUE)
    278   {
    279     GNUNET_break (0);
    280     return GNUNET_SYSERR;
    281   }
    282   return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO;
    283 }
    284 
    285 
    286 enum GNUNET_GenericReturnValue
    287 TALER_amount_max (struct TALER_Amount *ma,
    288                   const struct TALER_Amount *a1,
    289                   const struct TALER_Amount *a2)
    290 {
    291   if (GNUNET_OK !=
    292       TALER_amount_cmp_currency (a1,
    293                                  a2))
    294   {
    295     memset (ma,
    296             0,
    297             sizeof (*ma));
    298     return GNUNET_SYSERR;
    299   }
    300   if (1 == TALER_amount_cmp (a1,
    301                              a2))
    302     *ma = *a1;
    303   else
    304     *ma = *a2;
    305   return GNUNET_OK;
    306 }
    307 
    308 
    309 enum GNUNET_GenericReturnValue
    310 TALER_amount_min (struct TALER_Amount *mi,
    311                   const struct TALER_Amount *a1,
    312                   const struct TALER_Amount *a2)
    313 {
    314   if (GNUNET_OK !=
    315       TALER_amount_cmp_currency (a1,
    316                                  a2))
    317   {
    318     memset (mi,
    319             0,
    320             sizeof (*mi));
    321     return GNUNET_SYSERR;
    322   }
    323   if (1 == TALER_amount_cmp (a1,
    324                              a2))
    325     *mi = *a2;
    326   else
    327     *mi = *a1;
    328   return GNUNET_OK;
    329 }
    330 
    331 
    332 bool
    333 TALER_amount_is_zero (const struct TALER_Amount *amount)
    334 {
    335   if (GNUNET_OK !=
    336       TALER_amount_is_valid (amount))
    337     return false;
    338   return
    339     (0 == amount->value) &&
    340     (0 == amount->fraction);
    341 }
    342 
    343 
    344 enum GNUNET_GenericReturnValue
    345 TALER_amount_is_currency (const struct TALER_Amount *amount,
    346                           const char *currency)
    347 {
    348   if (GNUNET_OK !=
    349       TALER_amount_is_valid (amount))
    350     return GNUNET_SYSERR;
    351   return (0 == strcasecmp (currency,
    352                            amount->currency))
    353          ? GNUNET_OK
    354          : GNUNET_NO;
    355 }
    356 
    357 
    358 /**
    359  * Test if @a a is valid, NBO variant.
    360  *
    361  * @param a amount to test
    362  * @return #GNUNET_YES if valid,
    363  *         #GNUNET_NO if invalid
    364  */
    365 static enum GNUNET_GenericReturnValue
    366 test_valid_nbo (const struct TALER_AmountNBO *a)
    367 {
    368   return ('\0' != a->currency[0]) ? GNUNET_YES : GNUNET_NO;
    369 }
    370 
    371 
    372 enum GNUNET_GenericReturnValue
    373 TALER_amount_cmp_currency (const struct TALER_Amount *a1,
    374                            const struct TALER_Amount *a2)
    375 {
    376   if ( (GNUNET_NO == TALER_amount_is_valid (a1)) ||
    377        (GNUNET_NO == TALER_amount_is_valid (a2)) )
    378     return GNUNET_SYSERR;
    379   if (0 == strcasecmp (a1->currency,
    380                        a2->currency))
    381     return GNUNET_YES;
    382   return GNUNET_NO;
    383 }
    384 
    385 
    386 enum GNUNET_GenericReturnValue
    387 TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
    388                                const struct TALER_AmountNBO *a2)
    389 {
    390   if ( (GNUNET_NO == test_valid_nbo (a1)) ||
    391        (GNUNET_NO == test_valid_nbo (a2)) )
    392     return GNUNET_SYSERR;
    393   if (0 == strcasecmp (a1->currency,
    394                        a2->currency))
    395     return GNUNET_YES;
    396   return GNUNET_NO;
    397 }
    398 
    399 
    400 int
    401 TALER_amount_cmp (const struct TALER_Amount *a1,
    402                   const struct TALER_Amount *a2)
    403 {
    404   struct TALER_Amount n1;
    405   struct TALER_Amount n2;
    406 
    407   GNUNET_assert (GNUNET_YES ==
    408                  TALER_amount_cmp_currency (a1,
    409                                             a2));
    410   n1 = *a1;
    411   n2 = *a2;
    412   GNUNET_assert (GNUNET_SYSERR !=
    413                  TALER_amount_normalize (&n1));
    414   GNUNET_assert (GNUNET_SYSERR !=
    415                  TALER_amount_normalize (&n2));
    416   if (n1.value == n2.value)
    417   {
    418     if (n1.fraction < n2.fraction)
    419       return -1;
    420     if (n1.fraction > n2.fraction)
    421       return 1;
    422     return 0;
    423   }
    424   if (n1.value < n2.value)
    425     return -1;
    426   return 1;
    427 }
    428 
    429 
    430 int
    431 TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1,
    432                       const struct TALER_AmountNBO *a2)
    433 {
    434   struct TALER_Amount h1;
    435   struct TALER_Amount h2;
    436 
    437   TALER_amount_ntoh (&h1,
    438                      a1);
    439   TALER_amount_ntoh (&h2,
    440                      a2);
    441   return TALER_amount_cmp (&h1,
    442                            &h2);
    443 }
    444 
    445 
    446 enum TALER_AmountArithmeticResult
    447 TALER_amount_subtract (struct TALER_Amount *diff,
    448                        const struct TALER_Amount *a1,
    449                        const struct TALER_Amount *a2)
    450 {
    451   struct TALER_Amount n1;
    452   struct TALER_Amount n2;
    453 
    454   if (GNUNET_YES !=
    455       TALER_amount_cmp_currency (a1,
    456                                  a2))
    457   {
    458     invalidate (diff);
    459     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
    460   }
    461   /* make local copies to avoid aliasing problems between
    462      diff and a1/a2 */
    463   n1 = *a1;
    464   n2 = *a2;
    465   if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
    466        (GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
    467   {
    468     invalidate (diff);
    469     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
    470   }
    471 
    472   if (n1.fraction < n2.fraction)
    473   {
    474     if (0 == n1.value)
    475     {
    476       invalidate (diff);
    477       return TALER_AAR_INVALID_NEGATIVE_RESULT;
    478     }
    479     n1.fraction += TALER_AMOUNT_FRAC_BASE;
    480     n1.value--;
    481   }
    482   if (n1.value < n2.value)
    483   {
    484     invalidate (diff);
    485     return TALER_AAR_INVALID_NEGATIVE_RESULT;
    486   }
    487   GNUNET_assert (GNUNET_OK ==
    488                  TALER_amount_set_zero (n1.currency,
    489                                         diff));
    490   GNUNET_assert (n1.fraction >= n2.fraction);
    491   diff->fraction = n1.fraction - n2.fraction;
    492   GNUNET_assert (n1.value >= n2.value);
    493   diff->value = n1.value - n2.value;
    494   if ( (0 == diff->fraction) &&
    495        (0 == diff->value) )
    496     return TALER_AAR_RESULT_ZERO;
    497   return TALER_AAR_RESULT_POSITIVE;
    498 }
    499 
    500 
    501 enum TALER_AmountArithmeticResult
    502 TALER_amount_add (struct TALER_Amount *sum,
    503                   const struct TALER_Amount *a1,
    504                   const struct TALER_Amount *a2)
    505 {
    506   struct TALER_Amount n1;
    507   struct TALER_Amount n2;
    508   struct TALER_Amount res;
    509 
    510   if (GNUNET_YES !=
    511       TALER_amount_cmp_currency (a1,
    512                                  a2))
    513   {
    514     invalidate (sum);
    515     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
    516   }
    517   /* make local copies to avoid aliasing problems between
    518      diff and a1/a2 */
    519   n1 = *a1;
    520   n2 = *a2;
    521   if ( (GNUNET_SYSERR ==
    522         TALER_amount_normalize (&n1)) ||
    523        (GNUNET_SYSERR ==
    524         TALER_amount_normalize (&n2)) )
    525   {
    526     invalidate (sum);
    527     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
    528   }
    529 
    530   GNUNET_assert (GNUNET_OK ==
    531                  TALER_amount_set_zero (a1->currency,
    532                                         &res));
    533   res.value = n1.value + n2.value;
    534   if (res.value < n1.value)
    535   {
    536     /* integer overflow */
    537     invalidate (sum);
    538     return TALER_AAR_INVALID_RESULT_OVERFLOW;
    539   }
    540   if (res.value > TALER_AMOUNT_MAX_VALUE)
    541   {
    542     /* too large to be legal */
    543     invalidate (sum);
    544     return TALER_AAR_INVALID_RESULT_OVERFLOW;
    545   }
    546   res.fraction = n1.fraction + n2.fraction;
    547   if (GNUNET_SYSERR ==
    548       TALER_amount_normalize (&res))
    549   {
    550     /* integer overflow via carry from fraction */
    551     invalidate (sum);
    552     return TALER_AAR_INVALID_RESULT_OVERFLOW;
    553   }
    554   *sum = res;
    555   if ( (0 == sum->fraction) &&
    556        (0 == sum->value) )
    557     return TALER_AAR_RESULT_ZERO;
    558   return TALER_AAR_RESULT_POSITIVE;
    559 }
    560 
    561 
    562 enum GNUNET_GenericReturnValue
    563 TALER_amount_normalize (struct TALER_Amount *amount)
    564 {
    565   uint32_t overflow;
    566 
    567   if (GNUNET_YES != TALER_amount_is_valid (amount))
    568     return GNUNET_SYSERR;
    569   if (amount->fraction < TALER_AMOUNT_FRAC_BASE)
    570     return GNUNET_NO;
    571   overflow = amount->fraction / TALER_AMOUNT_FRAC_BASE;
    572   amount->fraction %= TALER_AMOUNT_FRAC_BASE;
    573   amount->value += overflow;
    574   if ( (amount->value < overflow) ||
    575        (amount->value > TALER_AMOUNT_MAX_VALUE) )
    576   {
    577     invalidate (amount);
    578     return GNUNET_SYSERR;
    579   }
    580   return GNUNET_OK;
    581 }
    582 
    583 
    584 /**
    585  * Convert the fraction of @a amount to a string in decimals.
    586  *
    587  * @param amount value to convert
    588  * @param[out] tail where to write the result
    589  */
    590 static void
    591 amount_to_tail (const struct TALER_Amount *amount,
    592                 char tail[TALER_AMOUNT_FRAC_LEN + 1])
    593 {
    594   uint32_t n = amount->fraction;
    595   unsigned int i;
    596 
    597   for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
    598   {
    599     tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10));
    600     n = (n * 10) % (TALER_AMOUNT_FRAC_BASE);
    601   }
    602   tail[i] = '\0';
    603 }
    604 
    605 
    606 char *
    607 TALER_amount_to_string (const struct TALER_Amount *amount)
    608 {
    609   char *result;
    610   struct TALER_Amount norm;
    611 
    612   if (GNUNET_YES != TALER_amount_is_valid (amount))
    613     return NULL;
    614   norm = *amount;
    615   GNUNET_break (GNUNET_SYSERR !=
    616                 TALER_amount_normalize (&norm));
    617   if (0 != norm.fraction)
    618   {
    619     char tail[TALER_AMOUNT_FRAC_LEN + 1];
    620 
    621     amount_to_tail (&norm,
    622                     tail);
    623     GNUNET_asprintf (&result,
    624                      "%s:%llu.%s",
    625                      norm.currency,
    626                      (unsigned long long) norm.value,
    627                      tail);
    628   }
    629   else
    630   {
    631     GNUNET_asprintf (&result,
    632                      "%s:%llu",
    633                      norm.currency,
    634                      (unsigned long long) norm.value);
    635   }
    636   return result;
    637 }
    638 
    639 
    640 const char *
    641 TALER_amount2s (const struct TALER_Amount *amount)
    642 {
    643   /* 24 is sufficient for a uint64_t value in decimal; 3 is for ":.\0" */
    644   static TALER_THREAD_LOCAL char result[TALER_AMOUNT_FRAC_LEN
    645                                         + TALER_CURRENCY_LEN + 3 + 24];
    646   struct TALER_Amount norm;
    647 
    648   if (GNUNET_YES != TALER_amount_is_valid (amount))
    649     return NULL;
    650   norm = *amount;
    651   GNUNET_break (GNUNET_SYSERR !=
    652                 TALER_amount_normalize (&norm));
    653   if (0 != norm.fraction)
    654   {
    655     char tail[TALER_AMOUNT_FRAC_LEN + 1];
    656 
    657     amount_to_tail (&norm,
    658                     tail);
    659     GNUNET_snprintf (result,
    660                      sizeof (result),
    661                      "%s:%llu.%s",
    662                      norm.currency,
    663                      (unsigned long long) norm.value,
    664                      tail);
    665   }
    666   else
    667   {
    668     GNUNET_snprintf (result,
    669                      sizeof (result),
    670                      "%s:%llu",
    671                      norm.currency,
    672                      (unsigned long long) norm.value);
    673   }
    674   return result;
    675 }
    676 
    677 
    678 void
    679 TALER_amount_divide (struct TALER_Amount *result,
    680                      const struct TALER_Amount *dividend,
    681                      uint32_t divisor)
    682 {
    683   uint64_t modr;
    684 
    685   GNUNET_assert (0 != divisor); /* division by zero is discouraged */
    686   *result = *dividend;
    687   /* in case @a dividend was not yet normalized */
    688   GNUNET_assert (GNUNET_SYSERR !=
    689                  TALER_amount_normalize (result));
    690   if (1 == divisor)
    691     return;
    692   modr = result->value % divisor;
    693   result->value /= divisor;
    694   /* modr fits into 32 bits, so we can safely multiply by (<32-bit) base and add fraction! */
    695   modr = (modr * TALER_AMOUNT_FRAC_BASE) + result->fraction;
    696   result->fraction = (uint32_t) (modr / divisor);
    697   /* 'fraction' could now be larger than #TALER_AMOUNT_FRAC_BASE, so we must normalize */
    698   GNUNET_assert (GNUNET_SYSERR !=
    699                  TALER_amount_normalize (result));
    700 }
    701 
    702 
    703 int
    704 TALER_amount_divide2 (const struct TALER_Amount *dividend,
    705                       const struct TALER_Amount *divisor)
    706 {
    707   double approx;
    708   double d;
    709   double r;
    710   int ret;
    711   struct TALER_Amount tmp;
    712   struct TALER_Amount nxt;
    713 
    714   if (GNUNET_YES !=
    715       TALER_amount_cmp_currency (dividend,
    716                                  divisor))
    717   {
    718     GNUNET_break (0);
    719     return -1;
    720   }
    721   if ( (0 == divisor->fraction) &&
    722        (0 == divisor->value) )
    723     return INT_MAX;
    724   /* first, get rounded approximation */
    725   d = ((double) dividend->value) * ((double) TALER_AMOUNT_FRAC_BASE)
    726       + ( (double) dividend->fraction);
    727   r = ((double) divisor->value) * ((double) TALER_AMOUNT_FRAC_BASE)
    728       + ( (double) divisor->fraction);
    729   approx = d / r;
    730   if (approx > ((double) INT_MAX))
    731     return INT_MAX; /* 'infinity' */
    732   /* round down */
    733   if (approx < 2)
    734     ret = 0;
    735   else
    736     ret = (int) approx - 2;
    737   /* Now do *exact* calculation, using well rounded-down factor as starting
    738      point to avoid having to do too many steps. */
    739   GNUNET_assert (0 <=
    740                  TALER_amount_multiply (&tmp,
    741                                         divisor,
    742                                         ret));
    743   /* in practice, this loop will only run for one or two iterations */
    744   while (1)
    745   {
    746     GNUNET_assert (0 <=
    747                    TALER_amount_add (&nxt,
    748                                      &tmp,
    749                                      divisor));
    750     if (1 ==
    751         TALER_amount_cmp (&nxt,
    752                           dividend))
    753       break; /* nxt > dividend */
    754     ret++;
    755     tmp = nxt;
    756   }
    757   return ret;
    758 }
    759 
    760 
    761 enum TALER_AmountArithmeticResult
    762 TALER_amount_multiply (struct TALER_Amount *result,
    763                        const struct TALER_Amount *amount,
    764                        uint32_t factor)
    765 {
    766   struct TALER_Amount in = *amount;
    767 
    768   if (GNUNET_SYSERR ==
    769       TALER_amount_normalize (&in))
    770     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
    771   GNUNET_memcpy (result->currency,
    772                  amount->currency,
    773                  TALER_CURRENCY_LEN);
    774   if ( (0 == factor) ||
    775        ( (0 == in.value) &&
    776          (0 == in.fraction) ) )
    777   {
    778     result->value = 0;
    779     result->fraction = 0;
    780     return TALER_AAR_RESULT_ZERO;
    781   }
    782   result->value = in.value * ((uint64_t) factor);
    783   if (in.value != result->value / factor)
    784     return TALER_AAR_INVALID_RESULT_OVERFLOW;
    785   {
    786     /* This multiplication cannot overflow since both inputs are 32-bit values */
    787     uint64_t tmp = ((uint64_t) factor) * ((uint64_t) in.fraction);
    788     uint64_t res;
    789 
    790     res = tmp / TALER_AMOUNT_FRAC_BASE;
    791     /* check for overflow */
    792     if (result->value + res < result->value)
    793       return TALER_AAR_INVALID_RESULT_OVERFLOW;
    794     result->value += res;
    795     result->fraction = tmp % TALER_AMOUNT_FRAC_BASE;
    796   }
    797   if (result->value > TALER_AMOUNT_MAX_VALUE)
    798     return TALER_AAR_INVALID_RESULT_OVERFLOW;
    799   /* This check should be redundant... */
    800   GNUNET_assert (GNUNET_SYSERR !=
    801                  TALER_amount_normalize (result));
    802   return TALER_AAR_RESULT_POSITIVE;
    803 }
    804 
    805 
    806 enum GNUNET_GenericReturnValue
    807 TALER_amount_round_down (struct TALER_Amount *amount,
    808                          const struct TALER_Amount *round_unit)
    809 {
    810   if (GNUNET_OK !=
    811       TALER_amount_cmp_currency (amount,
    812                                  round_unit))
    813   {
    814     GNUNET_break (0);
    815     return GNUNET_SYSERR;
    816   }
    817   if ( (0 != round_unit->fraction) &&
    818        (0 != round_unit->value) )
    819   {
    820     GNUNET_break (0);
    821     return GNUNET_SYSERR;
    822   }
    823   if ( (0 == round_unit->fraction) &&
    824        (0 == round_unit->value) )
    825     return GNUNET_NO; /* no rounding requested */
    826   if (0 != round_unit->fraction)
    827   {
    828     uint32_t delta;
    829 
    830     delta = amount->fraction % round_unit->fraction;
    831     if (0 == delta)
    832       return GNUNET_NO;
    833     amount->fraction -= delta;
    834   }
    835   if (0 != round_unit->value)
    836   {
    837     uint64_t delta;
    838 
    839     delta = amount->value % round_unit->value;
    840     if (0 == delta)
    841       return GNUNET_NO;
    842     amount->value -= delta;
    843     amount->fraction = 0;
    844   }
    845   return GNUNET_OK;
    846 }
    847 
    848 
    849 /* end of amount.c */