exchange

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

amount.c (21868B)


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