]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/cash.c
Make messages mentioning type names more uniform
[postgresql] / src / backend / utils / adt / cash.c
1 /*
2  * cash.c
3  * Written by D'Arcy J.M. Cain
4  * darcy@druid.net
5  * http://www.druid.net/darcy/
6  *
7  * Functions to allow input and output of money normally but store
8  * and handle it as 64 bit ints
9  *
10  * A slightly modified version of this file and a discussion of the
11  * workings can be found in the book "Software Solutions in C" by
12  * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7 except that
13  * this version handles 64 bit numbers and so can hold values up to
14  * $92,233,720,368,547,758.07.
15  *
16  * src/backend/utils/adt/cash.c
17  */
18
19 #include "postgres.h"
20
21 #include <limits.h>
22 #include <ctype.h>
23 #include <math.h>
24 #include <locale.h>
25
26 #include "libpq/pqformat.h"
27 #include "utils/builtins.h"
28 #include "utils/cash.h"
29 #include "utils/int8.h"
30 #include "utils/numeric.h"
31 #include "utils/pg_locale.h"
32
33
34 /*************************************************************************
35  * Private routines
36  ************************************************************************/
37
38 static const char *
39 num_word(Cash value)
40 {
41         static char buf[128];
42         static const char *small[] = {
43                 "zero", "one", "two", "three", "four", "five", "six", "seven",
44                 "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen",
45                 "fifteen", "sixteen", "seventeen", "eighteen", "nineteen", "twenty",
46                 "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"
47         };
48         const char **big = small + 18;
49         int                     tu = value % 100;
50
51         /* deal with the simple cases first */
52         if (value <= 20)
53                 return small[value];
54
55         /* is it an even multiple of 100? */
56         if (!tu)
57         {
58                 sprintf(buf, "%s hundred", small[value / 100]);
59                 return buf;
60         }
61
62         /* more than 99? */
63         if (value > 99)
64         {
65                 /* is it an even multiple of 10 other than 10? */
66                 if (value % 10 == 0 && tu > 10)
67                         sprintf(buf, "%s hundred %s",
68                                         small[value / 100], big[tu / 10]);
69                 else if (tu < 20)
70                         sprintf(buf, "%s hundred and %s",
71                                         small[value / 100], small[tu]);
72                 else
73                         sprintf(buf, "%s hundred %s %s",
74                                         small[value / 100], big[tu / 10], small[tu % 10]);
75         }
76         else
77         {
78                 /* is it an even multiple of 10 other than 10? */
79                 if (value % 10 == 0 && tu > 10)
80                         sprintf(buf, "%s", big[tu / 10]);
81                 else if (tu < 20)
82                         sprintf(buf, "%s", small[tu]);
83                 else
84                         sprintf(buf, "%s %s", big[tu / 10], small[tu % 10]);
85         }
86
87         return buf;
88 }       /* num_word() */
89
90 /* cash_in()
91  * Convert a string to a cash data type.
92  * Format is [$]###[,]###[.##]
93  * Examples: 123.45 $123.45 $123,456.78
94  *
95  */
96 Datum
97 cash_in(PG_FUNCTION_ARGS)
98 {
99         char       *str = PG_GETARG_CSTRING(0);
100         Cash            result;
101         Cash            value = 0;
102         Cash            dec = 0;
103         Cash            sgn = 1;
104         bool            seen_dot = false;
105         const char *s = str;
106         int                     fpoint;
107         char            dsymbol;
108         const char *ssymbol,
109                            *psymbol,
110                            *nsymbol,
111                            *csymbol;
112         struct lconv *lconvert = PGLC_localeconv();
113
114         /*
115          * frac_digits will be CHAR_MAX in some locales, notably C.  However, just
116          * testing for == CHAR_MAX is risky, because of compilers like gcc that
117          * "helpfully" let you alter the platform-standard definition of whether
118          * char is signed or not.  If we are so unfortunate as to get compiled
119          * with a nonstandard -fsigned-char or -funsigned-char switch, then our
120          * idea of CHAR_MAX will not agree with libc's. The safest course is not
121          * to test for CHAR_MAX at all, but to impose a range check for plausible
122          * frac_digits values.
123          */
124         fpoint = lconvert->frac_digits;
125         if (fpoint < 0 || fpoint > 10)
126                 fpoint = 2;                             /* best guess in this case, I think */
127
128         /* we restrict dsymbol to be a single byte, but not the other symbols */
129         if (*lconvert->mon_decimal_point != '\0' &&
130                 lconvert->mon_decimal_point[1] == '\0')
131                 dsymbol = *lconvert->mon_decimal_point;
132         else
133                 dsymbol = '.';
134         if (*lconvert->mon_thousands_sep != '\0')
135                 ssymbol = lconvert->mon_thousands_sep;
136         else    /* ssymbol should not equal dsymbol */
137                 ssymbol = (dsymbol != ',') ? "," : ".";
138         csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
139         psymbol = (*lconvert->positive_sign != '\0') ? lconvert->positive_sign : "+";
140         nsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
141
142 #ifdef CASHDEBUG
143         printf("cashin- precision '%d'; decimal '%c'; thousands '%s'; currency '%s'; positive '%s'; negative '%s'\n",
144                    fpoint, dsymbol, ssymbol, csymbol, psymbol, nsymbol);
145 #endif
146
147         /* we need to add all sorts of checking here.  For now just */
148         /* strip all leading whitespace and any leading currency symbol */
149         while (isspace((unsigned char) *s))
150                 s++;
151         if (strncmp(s, csymbol, strlen(csymbol)) == 0)
152                 s += strlen(csymbol);
153         while (isspace((unsigned char) *s))
154                 s++;
155
156 #ifdef CASHDEBUG
157         printf("cashin- string is '%s'\n", s);
158 #endif
159
160         /* a leading minus or paren signifies a negative number */
161         /* again, better heuristics needed */
162         /* XXX - doesn't properly check for balanced parens - djmc */
163         if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
164         {
165                 sgn = -1;
166                 s += strlen(nsymbol);
167         }
168         else if (*s == '(')
169         {
170                 sgn = -1;
171                 s++;
172         }
173         else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
174                 s += strlen(psymbol);
175
176 #ifdef CASHDEBUG
177         printf("cashin- string is '%s'\n", s);
178 #endif
179
180         /* allow whitespace and currency symbol after the sign, too */
181         while (isspace((unsigned char) *s))
182                 s++;
183         if (strncmp(s, csymbol, strlen(csymbol)) == 0)
184                 s += strlen(csymbol);
185         while (isspace((unsigned char) *s))
186                 s++;
187
188 #ifdef CASHDEBUG
189         printf("cashin- string is '%s'\n", s);
190 #endif
191
192         /*
193          * We accumulate the absolute amount in "value" and then apply the sign at
194          * the end.  (The sign can appear before or after the digits, so it would
195          * be more complicated to do otherwise.)  Because of the larger range of
196          * negative signed integers, we build "value" in the negative and then
197          * flip the sign at the end, catching most-negative-number overflow if
198          * necessary.
199          */
200
201         for (; *s; s++)
202         {
203                 /* we look for digits as long as we have found less */
204                 /* than the required number of decimal places */
205                 if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
206                 {
207                         Cash newvalue = (value * 10) - (*s - '0');
208
209                         if (newvalue / 10 != value)
210                                 ereport(ERROR,
211                                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
212                                                  errmsg("value \"%s\" is out of range for type %s",
213                                                                 str, "money")));
214
215                         value = newvalue;
216
217                         if (seen_dot)
218                                 dec++;
219                 }
220                 /* decimal point? then start counting fractions... */
221                 else if (*s == dsymbol && !seen_dot)
222                 {
223                         seen_dot = true;
224                 }
225                 /* ignore if "thousands" separator, else we're done */
226                 else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
227                         s += strlen(ssymbol) - 1;
228                 else
229                         break;
230         }
231
232         /* round off if there's another digit */
233         if (isdigit((unsigned char) *s) && *s >= '5')
234                 value--;  /* remember we build the value in the negative */
235
236         if (value > 0)
237                 ereport(ERROR,
238                                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
239                                  errmsg("value \"%s\" is out of range for type %s",
240                                                 str, "money")));
241
242         /* adjust for less than required decimal places */
243         for (; dec < fpoint; dec++)
244         {
245                 Cash newvalue = value * 10;
246
247                 if (newvalue / 10 != value)
248                         ereport(ERROR,
249                                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
250                                          errmsg("value \"%s\" is out of range for type %s",
251                                                         str, "money")));
252
253                 value = newvalue;
254         }
255
256         /*
257          * should only be trailing digits followed by whitespace, right paren,
258          * trailing sign, and/or trailing currency symbol
259          */
260         while (isdigit((unsigned char) *s))
261                 s++;
262
263         while (*s)
264         {
265                 if (isspace((unsigned char) *s) || *s == ')')
266                         s++;
267                 else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
268                 {
269                         sgn = -1;
270                         s += strlen(nsymbol);
271                 }
272                 else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
273                         s += strlen(psymbol);
274                 else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
275                         s += strlen(csymbol);
276                 else
277                         ereport(ERROR,
278                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
279                                          errmsg("invalid input syntax for type %s: \"%s\"",
280                                                         "money", str)));
281         }
282
283         /* If the value is supposed to be positive, flip the sign, but check for
284          * the most negative number. */
285         if (sgn > 0)
286         {
287                 result = -value;
288                 if (result < 0)
289                         ereport(ERROR,
290                                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
291                                          errmsg("value \"%s\" is out of range for type %s",
292                                                         str, "money")));
293         }
294         else
295                 result = value;
296
297 #ifdef CASHDEBUG
298         printf("cashin- result is " INT64_FORMAT "\n", result);
299 #endif
300
301         PG_RETURN_CASH(result);
302 }
303
304
305 /* cash_out()
306  * Function to convert cash to a dollars and cents representation, using
307  * the lc_monetary locale's formatting.
308  */
309 Datum
310 cash_out(PG_FUNCTION_ARGS)
311 {
312         Cash            value = PG_GETARG_CASH(0);
313         char       *result;
314         char            buf[128];
315         char       *bufptr;
316         int                     digit_pos;
317         int                     points,
318                                 mon_group;
319         char            dsymbol;
320         const char *ssymbol,
321                            *csymbol,
322                            *signsymbol;
323         char            sign_posn,
324                                 cs_precedes,
325                                 sep_by_space;
326         struct lconv *lconvert = PGLC_localeconv();
327
328         /* see comments about frac_digits in cash_in() */
329         points = lconvert->frac_digits;
330         if (points < 0 || points > 10)
331                 points = 2;                             /* best guess in this case, I think */
332
333         /*
334          * As with frac_digits, must apply a range check to mon_grouping to avoid
335          * being fooled by variant CHAR_MAX values.
336          */
337         mon_group = *lconvert->mon_grouping;
338         if (mon_group <= 0 || mon_group > 6)
339                 mon_group = 3;
340
341         /* we restrict dsymbol to be a single byte, but not the other symbols */
342         if (*lconvert->mon_decimal_point != '\0' &&
343                 lconvert->mon_decimal_point[1] == '\0')
344                 dsymbol = *lconvert->mon_decimal_point;
345         else
346                 dsymbol = '.';
347         if (*lconvert->mon_thousands_sep != '\0')
348                 ssymbol = lconvert->mon_thousands_sep;
349         else    /* ssymbol should not equal dsymbol */
350                 ssymbol = (dsymbol != ',') ? "," : ".";
351         csymbol = (*lconvert->currency_symbol != '\0') ? lconvert->currency_symbol : "$";
352
353         if (value < 0)
354         {
355                 /* make the amount positive for digit-reconstruction loop */
356                 value = -value;
357                 /* set up formatting data */
358                 signsymbol = (*lconvert->negative_sign != '\0') ? lconvert->negative_sign : "-";
359                 sign_posn = lconvert->n_sign_posn;
360                 cs_precedes = lconvert->n_cs_precedes;
361                 sep_by_space = lconvert->n_sep_by_space;
362         }
363         else
364         {
365                 signsymbol = lconvert->positive_sign;
366                 sign_posn = lconvert->p_sign_posn;
367                 cs_precedes = lconvert->p_cs_precedes;
368                 sep_by_space = lconvert->p_sep_by_space;
369         }
370
371         /* we build the digits+decimal-point+sep string right-to-left in buf[] */
372         bufptr = buf + sizeof(buf) - 1;
373         *bufptr = '\0';
374
375         /*
376          * Generate digits till there are no non-zero digits left and we emitted
377          * at least one to the left of the decimal point.  digit_pos is the
378          * current digit position, with zero as the digit just left of the decimal
379          * point, increasing to the right.
380          */
381         digit_pos = points;
382         do
383         {
384                 if (points && digit_pos == 0)
385                 {
386                         /* insert decimal point, but not if value cannot be fractional */
387                         *(--bufptr) = dsymbol;
388                 }
389                 else if (digit_pos < 0 && (digit_pos % mon_group) == 0)
390                 {
391                         /* insert thousands sep, but only to left of radix point */
392                         bufptr -= strlen(ssymbol);
393                         memcpy(bufptr, ssymbol, strlen(ssymbol));
394                 }
395
396                 *(--bufptr) = ((uint64) value % 10) + '0';
397                 value = ((uint64) value) / 10;
398                 digit_pos--;
399         } while (value || digit_pos >= 0);
400
401         /*----------
402          * Now, attach currency symbol and sign symbol in the correct order.
403          *
404          * The POSIX spec defines these values controlling this code:
405          *
406          * p/n_sign_posn:
407          *      0       Parentheses enclose the quantity and the currency_symbol.
408          *      1       The sign string precedes the quantity and the currency_symbol.
409          *      2       The sign string succeeds the quantity and the currency_symbol.
410          *      3       The sign string precedes the currency_symbol.
411          *      4       The sign string succeeds the currency_symbol.
412          *
413          * p/n_cs_precedes: 0 means currency symbol after value, else before it.
414          *
415          * p/n_sep_by_space:
416          *      0       No <space> separates the currency symbol and value.
417          *      1       If the currency symbol and sign string are adjacent, a <space>
418          *              separates them from the value; otherwise, a <space> separates
419          *              the currency symbol from the value.
420          *      2       If the currency symbol and sign string are adjacent, a <space>
421          *              separates them; otherwise, a <space> separates the sign string
422          *              from the value.
423          *----------
424          */
425         switch (sign_posn)
426         {
427                 case 0:
428                         if (cs_precedes)
429                                 result = psprintf("(%s%s%s)",
430                                                                   csymbol,
431                                                                   (sep_by_space == 1) ? " " : "",
432                                                                   bufptr);
433                         else
434                                 result = psprintf("(%s%s%s)",
435                                                                   bufptr,
436                                                                   (sep_by_space == 1) ? " " : "",
437                                                                   csymbol);
438                         break;
439                 case 1:
440                 default:
441                         if (cs_precedes)
442                                 result = psprintf("%s%s%s%s%s",
443                                                                   signsymbol,
444                                                                   (sep_by_space == 2) ? " " : "",
445                                                                   csymbol,
446                                                                   (sep_by_space == 1) ? " " : "",
447                                                                   bufptr);
448                         else
449                                 result = psprintf("%s%s%s%s%s",
450                                                                   signsymbol,
451                                                                   (sep_by_space == 2) ? " " : "",
452                                                                   bufptr,
453                                                                   (sep_by_space == 1) ? " " : "",
454                                                                   csymbol);
455                         break;
456                 case 2:
457                         if (cs_precedes)
458                                 result = psprintf("%s%s%s%s%s",
459                                                                   csymbol,
460                                                                   (sep_by_space == 1) ? " " : "",
461                                                                   bufptr,
462                                                                   (sep_by_space == 2) ? " " : "",
463                                                                   signsymbol);
464                         else
465                                 result = psprintf("%s%s%s%s%s",
466                                                                   bufptr,
467                                                                   (sep_by_space == 1) ? " " : "",
468                                                                   csymbol,
469                                                                   (sep_by_space == 2) ? " " : "",
470                                                                   signsymbol);
471                         break;
472                 case 3:
473                         if (cs_precedes)
474                                 result = psprintf("%s%s%s%s%s",
475                                                                   signsymbol,
476                                                                   (sep_by_space == 2) ? " " : "",
477                                                                   csymbol,
478                                                                   (sep_by_space == 1) ? " " : "",
479                                                                   bufptr);
480                         else
481                                 result = psprintf("%s%s%s%s%s",
482                                                                   bufptr,
483                                                                   (sep_by_space == 1) ? " " : "",
484                                                                   signsymbol,
485                                                                   (sep_by_space == 2) ? " " : "",
486                                                                   csymbol);
487                         break;
488                 case 4:
489                         if (cs_precedes)
490                                 result = psprintf("%s%s%s%s%s",
491                                                                   csymbol,
492                                                                   (sep_by_space == 2) ? " " : "",
493                                                                   signsymbol,
494                                                                   (sep_by_space == 1) ? " " : "",
495                                                                   bufptr);
496                         else
497                                 result = psprintf("%s%s%s%s%s",
498                                                                   bufptr,
499                                                                   (sep_by_space == 1) ? " " : "",
500                                                                   csymbol,
501                                                                   (sep_by_space == 2) ? " " : "",
502                                                                   signsymbol);
503                         break;
504         }
505
506         PG_RETURN_CSTRING(result);
507 }
508
509 /*
510  *              cash_recv                       - converts external binary format to cash
511  */
512 Datum
513 cash_recv(PG_FUNCTION_ARGS)
514 {
515         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
516
517         PG_RETURN_CASH((Cash) pq_getmsgint64(buf));
518 }
519
520 /*
521  *              cash_send                       - converts cash to binary format
522  */
523 Datum
524 cash_send(PG_FUNCTION_ARGS)
525 {
526         Cash            arg1 = PG_GETARG_CASH(0);
527         StringInfoData buf;
528
529         pq_begintypsend(&buf);
530         pq_sendint64(&buf, arg1);
531         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
532 }
533
534 /*
535  * Comparison functions
536  */
537
538 Datum
539 cash_eq(PG_FUNCTION_ARGS)
540 {
541         Cash            c1 = PG_GETARG_CASH(0);
542         Cash            c2 = PG_GETARG_CASH(1);
543
544         PG_RETURN_BOOL(c1 == c2);
545 }
546
547 Datum
548 cash_ne(PG_FUNCTION_ARGS)
549 {
550         Cash            c1 = PG_GETARG_CASH(0);
551         Cash            c2 = PG_GETARG_CASH(1);
552
553         PG_RETURN_BOOL(c1 != c2);
554 }
555
556 Datum
557 cash_lt(PG_FUNCTION_ARGS)
558 {
559         Cash            c1 = PG_GETARG_CASH(0);
560         Cash            c2 = PG_GETARG_CASH(1);
561
562         PG_RETURN_BOOL(c1 < c2);
563 }
564
565 Datum
566 cash_le(PG_FUNCTION_ARGS)
567 {
568         Cash            c1 = PG_GETARG_CASH(0);
569         Cash            c2 = PG_GETARG_CASH(1);
570
571         PG_RETURN_BOOL(c1 <= c2);
572 }
573
574 Datum
575 cash_gt(PG_FUNCTION_ARGS)
576 {
577         Cash            c1 = PG_GETARG_CASH(0);
578         Cash            c2 = PG_GETARG_CASH(1);
579
580         PG_RETURN_BOOL(c1 > c2);
581 }
582
583 Datum
584 cash_ge(PG_FUNCTION_ARGS)
585 {
586         Cash            c1 = PG_GETARG_CASH(0);
587         Cash            c2 = PG_GETARG_CASH(1);
588
589         PG_RETURN_BOOL(c1 >= c2);
590 }
591
592 Datum
593 cash_cmp(PG_FUNCTION_ARGS)
594 {
595         Cash            c1 = PG_GETARG_CASH(0);
596         Cash            c2 = PG_GETARG_CASH(1);
597
598         if (c1 > c2)
599                 PG_RETURN_INT32(1);
600         else if (c1 == c2)
601                 PG_RETURN_INT32(0);
602         else
603                 PG_RETURN_INT32(-1);
604 }
605
606
607 /* cash_pl()
608  * Add two cash values.
609  */
610 Datum
611 cash_pl(PG_FUNCTION_ARGS)
612 {
613         Cash            c1 = PG_GETARG_CASH(0);
614         Cash            c2 = PG_GETARG_CASH(1);
615         Cash            result;
616
617         result = c1 + c2;
618
619         PG_RETURN_CASH(result);
620 }
621
622
623 /* cash_mi()
624  * Subtract two cash values.
625  */
626 Datum
627 cash_mi(PG_FUNCTION_ARGS)
628 {
629         Cash            c1 = PG_GETARG_CASH(0);
630         Cash            c2 = PG_GETARG_CASH(1);
631         Cash            result;
632
633         result = c1 - c2;
634
635         PG_RETURN_CASH(result);
636 }
637
638
639 /* cash_div_cash()
640  * Divide cash by cash, returning float8.
641  */
642 Datum
643 cash_div_cash(PG_FUNCTION_ARGS)
644 {
645         Cash            dividend = PG_GETARG_CASH(0);
646         Cash            divisor = PG_GETARG_CASH(1);
647         float8          quotient;
648
649         if (divisor == 0)
650                 ereport(ERROR,
651                                 (errcode(ERRCODE_DIVISION_BY_ZERO),
652                                  errmsg("division by zero")));
653
654         quotient = (float8) dividend / (float8) divisor;
655         PG_RETURN_FLOAT8(quotient);
656 }
657
658
659 /* cash_mul_flt8()
660  * Multiply cash by float8.
661  */
662 Datum
663 cash_mul_flt8(PG_FUNCTION_ARGS)
664 {
665         Cash            c = PG_GETARG_CASH(0);
666         float8          f = PG_GETARG_FLOAT8(1);
667         Cash            result;
668
669         result = c * f;
670         PG_RETURN_CASH(result);
671 }
672
673
674 /* flt8_mul_cash()
675  * Multiply float8 by cash.
676  */
677 Datum
678 flt8_mul_cash(PG_FUNCTION_ARGS)
679 {
680         float8          f = PG_GETARG_FLOAT8(0);
681         Cash            c = PG_GETARG_CASH(1);
682         Cash            result;
683
684         result = f * c;
685         PG_RETURN_CASH(result);
686 }
687
688
689 /* cash_div_flt8()
690  * Divide cash by float8.
691  */
692 Datum
693 cash_div_flt8(PG_FUNCTION_ARGS)
694 {
695         Cash            c = PG_GETARG_CASH(0);
696         float8          f = PG_GETARG_FLOAT8(1);
697         Cash            result;
698
699         if (f == 0.0)
700                 ereport(ERROR,
701                                 (errcode(ERRCODE_DIVISION_BY_ZERO),
702                                  errmsg("division by zero")));
703
704         result = rint(c / f);
705         PG_RETURN_CASH(result);
706 }
707
708
709 /* cash_mul_flt4()
710  * Multiply cash by float4.
711  */
712 Datum
713 cash_mul_flt4(PG_FUNCTION_ARGS)
714 {
715         Cash            c = PG_GETARG_CASH(0);
716         float4          f = PG_GETARG_FLOAT4(1);
717         Cash            result;
718
719         result = c * f;
720         PG_RETURN_CASH(result);
721 }
722
723
724 /* flt4_mul_cash()
725  * Multiply float4 by cash.
726  */
727 Datum
728 flt4_mul_cash(PG_FUNCTION_ARGS)
729 {
730         float4          f = PG_GETARG_FLOAT4(0);
731         Cash            c = PG_GETARG_CASH(1);
732         Cash            result;
733
734         result = f * c;
735         PG_RETURN_CASH(result);
736 }
737
738
739 /* cash_div_flt4()
740  * Divide cash by float4.
741  *
742  */
743 Datum
744 cash_div_flt4(PG_FUNCTION_ARGS)
745 {
746         Cash            c = PG_GETARG_CASH(0);
747         float4          f = PG_GETARG_FLOAT4(1);
748         Cash            result;
749
750         if (f == 0.0)
751                 ereport(ERROR,
752                                 (errcode(ERRCODE_DIVISION_BY_ZERO),
753                                  errmsg("division by zero")));
754
755         result = rint(c / f);
756         PG_RETURN_CASH(result);
757 }
758
759
760 /* cash_mul_int8()
761  * Multiply cash by int8.
762  */
763 Datum
764 cash_mul_int8(PG_FUNCTION_ARGS)
765 {
766         Cash            c = PG_GETARG_CASH(0);
767         int64           i = PG_GETARG_INT64(1);
768         Cash            result;
769
770         result = c * i;
771         PG_RETURN_CASH(result);
772 }
773
774
775 /* int8_mul_cash()
776  * Multiply int8 by cash.
777  */
778 Datum
779 int8_mul_cash(PG_FUNCTION_ARGS)
780 {
781         int64           i = PG_GETARG_INT64(0);
782         Cash            c = PG_GETARG_CASH(1);
783         Cash            result;
784
785         result = i * c;
786         PG_RETURN_CASH(result);
787 }
788
789 /* cash_div_int8()
790  * Divide cash by 8-byte integer.
791  */
792 Datum
793 cash_div_int8(PG_FUNCTION_ARGS)
794 {
795         Cash            c = PG_GETARG_CASH(0);
796         int64           i = PG_GETARG_INT64(1);
797         Cash            result;
798
799         if (i == 0)
800                 ereport(ERROR,
801                                 (errcode(ERRCODE_DIVISION_BY_ZERO),
802                                  errmsg("division by zero")));
803
804         result = rint(c / i);
805
806         PG_RETURN_CASH(result);
807 }
808
809
810 /* cash_mul_int4()
811  * Multiply cash by int4.
812  */
813 Datum
814 cash_mul_int4(PG_FUNCTION_ARGS)
815 {
816         Cash            c = PG_GETARG_CASH(0);
817         int32           i = PG_GETARG_INT32(1);
818         Cash            result;
819
820         result = c * i;
821         PG_RETURN_CASH(result);
822 }
823
824
825 /* int4_mul_cash()
826  * Multiply int4 by cash.
827  */
828 Datum
829 int4_mul_cash(PG_FUNCTION_ARGS)
830 {
831         int32           i = PG_GETARG_INT32(0);
832         Cash            c = PG_GETARG_CASH(1);
833         Cash            result;
834
835         result = i * c;
836         PG_RETURN_CASH(result);
837 }
838
839
840 /* cash_div_int4()
841  * Divide cash by 4-byte integer.
842  *
843  */
844 Datum
845 cash_div_int4(PG_FUNCTION_ARGS)
846 {
847         Cash            c = PG_GETARG_CASH(0);
848         int32           i = PG_GETARG_INT32(1);
849         Cash            result;
850
851         if (i == 0)
852                 ereport(ERROR,
853                                 (errcode(ERRCODE_DIVISION_BY_ZERO),
854                                  errmsg("division by zero")));
855
856         result = rint(c / i);
857
858         PG_RETURN_CASH(result);
859 }
860
861
862 /* cash_mul_int2()
863  * Multiply cash by int2.
864  */
865 Datum
866 cash_mul_int2(PG_FUNCTION_ARGS)
867 {
868         Cash            c = PG_GETARG_CASH(0);
869         int16           s = PG_GETARG_INT16(1);
870         Cash            result;
871
872         result = c * s;
873         PG_RETURN_CASH(result);
874 }
875
876 /* int2_mul_cash()
877  * Multiply int2 by cash.
878  */
879 Datum
880 int2_mul_cash(PG_FUNCTION_ARGS)
881 {
882         int16           s = PG_GETARG_INT16(0);
883         Cash            c = PG_GETARG_CASH(1);
884         Cash            result;
885
886         result = s * c;
887         PG_RETURN_CASH(result);
888 }
889
890 /* cash_div_int2()
891  * Divide cash by int2.
892  *
893  */
894 Datum
895 cash_div_int2(PG_FUNCTION_ARGS)
896 {
897         Cash            c = PG_GETARG_CASH(0);
898         int16           s = PG_GETARG_INT16(1);
899         Cash            result;
900
901         if (s == 0)
902                 ereport(ERROR,
903                                 (errcode(ERRCODE_DIVISION_BY_ZERO),
904                                  errmsg("division by zero")));
905
906         result = rint(c / s);
907         PG_RETURN_CASH(result);
908 }
909
910 /* cashlarger()
911  * Return larger of two cash values.
912  */
913 Datum
914 cashlarger(PG_FUNCTION_ARGS)
915 {
916         Cash            c1 = PG_GETARG_CASH(0);
917         Cash            c2 = PG_GETARG_CASH(1);
918         Cash            result;
919
920         result = (c1 > c2) ? c1 : c2;
921
922         PG_RETURN_CASH(result);
923 }
924
925 /* cashsmaller()
926  * Return smaller of two cash values.
927  */
928 Datum
929 cashsmaller(PG_FUNCTION_ARGS)
930 {
931         Cash            c1 = PG_GETARG_CASH(0);
932         Cash            c2 = PG_GETARG_CASH(1);
933         Cash            result;
934
935         result = (c1 < c2) ? c1 : c2;
936
937         PG_RETURN_CASH(result);
938 }
939
940 /* cash_words()
941  * This converts an int4 as well but to a representation using words
942  * Obviously way North American centric - sorry
943  */
944 Datum
945 cash_words(PG_FUNCTION_ARGS)
946 {
947         Cash            value = PG_GETARG_CASH(0);
948         uint64          val;
949         char            buf[256];
950         char       *p = buf;
951         Cash            m0;
952         Cash            m1;
953         Cash            m2;
954         Cash            m3;
955         Cash            m4;
956         Cash            m5;
957         Cash            m6;
958
959         /* work with positive numbers */
960         if (value < 0)
961         {
962                 value = -value;
963                 strcpy(buf, "minus ");
964                 p += 6;
965         }
966         else
967                 buf[0] = '\0';
968
969         /* Now treat as unsigned, to avoid trouble at INT_MIN */
970         val = (uint64) value;
971
972         m0 = val % INT64CONST(100); /* cents */
973         m1 = (val / INT64CONST(100)) % 1000;            /* hundreds */
974         m2 = (val / INT64CONST(100000)) % 1000;         /* thousands */
975         m3 = (val / INT64CONST(100000000)) % 1000;      /* millions */
976         m4 = (val / INT64CONST(100000000000)) % 1000;           /* billions */
977         m5 = (val / INT64CONST(100000000000000)) % 1000;        /* trillions */
978         m6 = (val / INT64CONST(100000000000000000)) % 1000; /* quadrillions */
979
980         if (m6)
981         {
982                 strcat(buf, num_word(m6));
983                 strcat(buf, " quadrillion ");
984         }
985
986         if (m5)
987         {
988                 strcat(buf, num_word(m5));
989                 strcat(buf, " trillion ");
990         }
991
992         if (m4)
993         {
994                 strcat(buf, num_word(m4));
995                 strcat(buf, " billion ");
996         }
997
998         if (m3)
999         {
1000                 strcat(buf, num_word(m3));
1001                 strcat(buf, " million ");
1002         }
1003
1004         if (m2)
1005         {
1006                 strcat(buf, num_word(m2));
1007                 strcat(buf, " thousand ");
1008         }
1009
1010         if (m1)
1011                 strcat(buf, num_word(m1));
1012
1013         if (!*p)
1014                 strcat(buf, "zero");
1015
1016         strcat(buf, (val / 100) == 1 ? " dollar and " : " dollars and ");
1017         strcat(buf, num_word(m0));
1018         strcat(buf, m0 == 1 ? " cent" : " cents");
1019
1020         /* capitalize output */
1021         buf[0] = pg_toupper((unsigned char) buf[0]);
1022
1023         /* return as text datum */
1024         PG_RETURN_TEXT_P(cstring_to_text(buf));
1025 }
1026
1027
1028 /* cash_numeric()
1029  * Convert cash to numeric.
1030  */
1031 Datum
1032 cash_numeric(PG_FUNCTION_ARGS)
1033 {
1034         Cash            money = PG_GETARG_CASH(0);
1035         Numeric         result;
1036         int                     fpoint;
1037         int64           scale;
1038         int                     i;
1039         Datum           amount;
1040         Datum           numeric_scale;
1041         Datum           quotient;
1042         struct lconv *lconvert = PGLC_localeconv();
1043
1044         /* see comments about frac_digits in cash_in() */
1045         fpoint = lconvert->frac_digits;
1046         if (fpoint < 0 || fpoint > 10)
1047                 fpoint = 2;
1048
1049         /* compute required scale factor */
1050         scale = 1;
1051         for (i = 0; i < fpoint; i++)
1052                 scale *= 10;
1053
1054         /* form the result as money / scale */
1055         amount = DirectFunctionCall1(int8_numeric, Int64GetDatum(money));
1056         numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1057         quotient = DirectFunctionCall2(numeric_div, amount, numeric_scale);
1058
1059         /* forcibly round to exactly the intended number of digits */
1060         result = DatumGetNumeric(DirectFunctionCall2(numeric_round,
1061                                                                                                  quotient,
1062                                                                                                  Int32GetDatum(fpoint)));
1063
1064         PG_RETURN_NUMERIC(result);
1065 }
1066
1067 /* numeric_cash()
1068  * Convert numeric to cash.
1069  */
1070 Datum
1071 numeric_cash(PG_FUNCTION_ARGS)
1072 {
1073         Datum           amount = PG_GETARG_DATUM(0);
1074         Cash            result;
1075         int                     fpoint;
1076         int64           scale;
1077         int                     i;
1078         Datum           numeric_scale;
1079         struct lconv *lconvert = PGLC_localeconv();
1080
1081         /* see comments about frac_digits in cash_in() */
1082         fpoint = lconvert->frac_digits;
1083         if (fpoint < 0 || fpoint > 10)
1084                 fpoint = 2;
1085
1086         /* compute required scale factor */
1087         scale = 1;
1088         for (i = 0; i < fpoint; i++)
1089                 scale *= 10;
1090
1091         /* multiply the input amount by scale factor */
1092         numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1093         amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
1094
1095         /* note that numeric_int8 will round to nearest integer for us */
1096         result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
1097
1098         PG_RETURN_CASH(result);
1099 }
1100
1101 /* int4_cash()
1102  * Convert int4 (int) to cash
1103  */
1104 Datum
1105 int4_cash(PG_FUNCTION_ARGS)
1106 {
1107         int32           amount = PG_GETARG_INT32(0);
1108         Cash            result;
1109         int                     fpoint;
1110         int64           scale;
1111         int                     i;
1112         struct lconv *lconvert = PGLC_localeconv();
1113
1114         /* see comments about frac_digits in cash_in() */
1115         fpoint = lconvert->frac_digits;
1116         if (fpoint < 0 || fpoint > 10)
1117                 fpoint = 2;
1118
1119         /* compute required scale factor */
1120         scale = 1;
1121         for (i = 0; i < fpoint; i++)
1122                 scale *= 10;
1123
1124         /* compute amount * scale, checking for overflow */
1125         result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1126                                                                                            Int64GetDatum(scale)));
1127
1128         PG_RETURN_CASH(result);
1129 }
1130
1131 /* int8_cash()
1132  * Convert int8 (bigint) to cash
1133  */
1134 Datum
1135 int8_cash(PG_FUNCTION_ARGS)
1136 {
1137         int64           amount = PG_GETARG_INT64(0);
1138         Cash            result;
1139         int                     fpoint;
1140         int64           scale;
1141         int                     i;
1142         struct lconv *lconvert = PGLC_localeconv();
1143
1144         /* see comments about frac_digits in cash_in() */
1145         fpoint = lconvert->frac_digits;
1146         if (fpoint < 0 || fpoint > 10)
1147                 fpoint = 2;
1148
1149         /* compute required scale factor */
1150         scale = 1;
1151         for (i = 0; i < fpoint; i++)
1152                 scale *= 10;
1153
1154         /* compute amount * scale, checking for overflow */
1155         result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1156                                                                                            Int64GetDatum(scale)));
1157
1158         PG_RETURN_CASH(result);
1159 }