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 
25 #include "common/int.h"
26 #include "libpq/pqformat.h"
27 #include "utils/builtins.h"
pg_to_ascii(unsigned char * src,unsigned char * src_end,unsigned char * dest,int enc)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 *const 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 *const *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;
encode_to_ascii(text * data,int enc)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
to_ascii_encname(PG_FUNCTION_ARGS)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 */
to_ascii_enc(PG_FUNCTION_ARGS)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++;
to_ascii_default(PG_FUNCTION_ARGS)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 	}
ascii_safe_strlcpy(char * dest,const char * src,size_t destsiz)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 		/*
204 		 * We look for digits as long as we have found less than the required
205 		 * number of decimal places.
206 		 */
207 		if (isdigit((unsigned char) *s) && (!seen_dot || dec < fpoint))
208 		{
209 			int8		digit = *s - '0';
210 
211 			if (pg_mul_s64_overflow(value, 10, &value) ||
212 				pg_sub_s64_overflow(value, digit, &value))
213 				ereport(ERROR,
214 						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
215 						 errmsg("value \"%s\" is out of range for type %s",
216 								str, "money")));
217 
218 			if (seen_dot)
219 				dec++;
220 		}
221 		/* decimal point? then start counting fractions... */
222 		else if (*s == dsymbol && !seen_dot)
223 		{
224 			seen_dot = true;
225 		}
226 		/* ignore if "thousands" separator, else we're done */
227 		else if (strncmp(s, ssymbol, strlen(ssymbol)) == 0)
228 			s += strlen(ssymbol) - 1;
229 		else
230 			break;
231 	}
232 
233 	/* round off if there's another digit */
234 	if (isdigit((unsigned char) *s) && *s >= '5')
235 	{
236 		/* remember we build the value in the negative */
237 		if (pg_sub_s64_overflow(value, 1, &value))
238 			ereport(ERROR,
239 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
240 					 errmsg("value \"%s\" is out of range for type %s",
241 							str, "money")));
242 	}
243 
244 	/* adjust for less than required decimal places */
245 	for (; dec < fpoint; dec++)
246 	{
247 		if (pg_mul_s64_overflow(value, 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 
254 	/*
255 	 * should only be trailing digits followed by whitespace, right paren,
256 	 * trailing sign, and/or trailing currency symbol
257 	 */
258 	while (isdigit((unsigned char) *s))
259 		s++;
260 
261 	while (*s)
262 	{
263 		if (isspace((unsigned char) *s) || *s == ')')
264 			s++;
265 		else if (strncmp(s, nsymbol, strlen(nsymbol)) == 0)
266 		{
267 			sgn = -1;
268 			s += strlen(nsymbol);
269 		}
270 		else if (strncmp(s, psymbol, strlen(psymbol)) == 0)
271 			s += strlen(psymbol);
272 		else if (strncmp(s, csymbol, strlen(csymbol)) == 0)
273 			s += strlen(csymbol);
274 		else
275 			ereport(ERROR,
276 					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
277 					 errmsg("invalid input syntax for type %s: \"%s\"",
278 							"money", str)));
279 	}
280 
281 	/*
282 	 * If the value is supposed to be positive, flip the sign, but check for
283 	 * the most negative number.
284 	 */
285 	if (sgn > 0)
286 	{
287 		if (value == PG_INT64_MIN)
288 			ereport(ERROR,
289 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
290 					 errmsg("value \"%s\" is out of range for type %s",
291 							str, "money")));
292 		result = -value;
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 = rint(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 = rint(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 = rint(c * (float8) 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 = rint((float8) 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 / (float8) 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 = 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 = 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 = 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 	Datum		result;
1036 	int			fpoint;
1037 	struct lconv *lconvert = PGLC_localeconv();
1038 
1039 	/* see comments about frac_digits in cash_in() */
1040 	fpoint = lconvert->frac_digits;
1041 	if (fpoint < 0 || fpoint > 10)
1042 		fpoint = 2;
1043 
1044 	/* convert the integral money value to numeric */
1045 	result = DirectFunctionCall1(int8_numeric, Int64GetDatum(money));
1046 
1047 	/* scale appropriately, if needed */
1048 	if (fpoint > 0)
1049 	{
1050 		int64		scale;
1051 		int			i;
1052 		Datum		numeric_scale;
1053 		Datum		quotient;
1054 
1055 		/* compute required scale factor */
1056 		scale = 1;
1057 		for (i = 0; i < fpoint; i++)
1058 			scale *= 10;
1059 		numeric_scale = DirectFunctionCall1(int8_numeric,
1060 											Int64GetDatum(scale));
1061 
1062 		/*
1063 		 * Given integral inputs approaching INT64_MAX, select_div_scale()
1064 		 * might choose a result scale of zero, causing loss of fractional
1065 		 * digits in the quotient.  We can ensure an exact result by setting
1066 		 * the dscale of either input to be at least as large as the desired
1067 		 * result scale.  numeric_round() will do that for us.
1068 		 */
1069 		numeric_scale = DirectFunctionCall2(numeric_round,
1070 											numeric_scale,
1071 											Int32GetDatum(fpoint));
1072 
1073 		/* Now we can safely divide ... */
1074 		quotient = DirectFunctionCall2(numeric_div, result, numeric_scale);
1075 
1076 		/* ... and forcibly round to exactly the intended number of digits */
1077 		result = DirectFunctionCall2(numeric_round,
1078 									 quotient,
1079 									 Int32GetDatum(fpoint));
1080 	}
1081 
1082 	PG_RETURN_DATUM(result);
1083 }
1084 
1085 /* numeric_cash()
1086  * Convert numeric to cash.
1087  */
1088 Datum
1089 numeric_cash(PG_FUNCTION_ARGS)
1090 {
1091 	Datum		amount = PG_GETARG_DATUM(0);
1092 	Cash		result;
1093 	int			fpoint;
1094 	int64		scale;
1095 	int			i;
1096 	Datum		numeric_scale;
1097 	struct lconv *lconvert = PGLC_localeconv();
1098 
1099 	/* see comments about frac_digits in cash_in() */
1100 	fpoint = lconvert->frac_digits;
1101 	if (fpoint < 0 || fpoint > 10)
1102 		fpoint = 2;
1103 
1104 	/* compute required scale factor */
1105 	scale = 1;
1106 	for (i = 0; i < fpoint; i++)
1107 		scale *= 10;
1108 
1109 	/* multiply the input amount by scale factor */
1110 	numeric_scale = DirectFunctionCall1(int8_numeric, Int64GetDatum(scale));
1111 	amount = DirectFunctionCall2(numeric_mul, amount, numeric_scale);
1112 
1113 	/* note that numeric_int8 will round to nearest integer for us */
1114 	result = DatumGetInt64(DirectFunctionCall1(numeric_int8, amount));
1115 
1116 	PG_RETURN_CASH(result);
1117 }
1118 
1119 /* int4_cash()
1120  * Convert int4 (int) to cash
1121  */
1122 Datum
1123 int4_cash(PG_FUNCTION_ARGS)
1124 {
1125 	int32		amount = PG_GETARG_INT32(0);
1126 	Cash		result;
1127 	int			fpoint;
1128 	int64		scale;
1129 	int			i;
1130 	struct lconv *lconvert = PGLC_localeconv();
1131 
1132 	/* see comments about frac_digits in cash_in() */
1133 	fpoint = lconvert->frac_digits;
1134 	if (fpoint < 0 || fpoint > 10)
1135 		fpoint = 2;
1136 
1137 	/* compute required scale factor */
1138 	scale = 1;
1139 	for (i = 0; i < fpoint; i++)
1140 		scale *= 10;
1141 
1142 	/* compute amount * scale, checking for overflow */
1143 	result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1144 											   Int64GetDatum(scale)));
1145 
1146 	PG_RETURN_CASH(result);
1147 }
1148 
1149 /* int8_cash()
1150  * Convert int8 (bigint) to cash
1151  */
1152 Datum
1153 int8_cash(PG_FUNCTION_ARGS)
1154 {
1155 	int64		amount = PG_GETARG_INT64(0);
1156 	Cash		result;
1157 	int			fpoint;
1158 	int64		scale;
1159 	int			i;
1160 	struct lconv *lconvert = PGLC_localeconv();
1161 
1162 	/* see comments about frac_digits in cash_in() */
1163 	fpoint = lconvert->frac_digits;
1164 	if (fpoint < 0 || fpoint > 10)
1165 		fpoint = 2;
1166 
1167 	/* compute required scale factor */
1168 	scale = 1;
1169 	for (i = 0; i < fpoint; i++)
1170 		scale *= 10;
1171 
1172 	/* compute amount * scale, checking for overflow */
1173 	result = DatumGetInt64(DirectFunctionCall2(int8mul, Int64GetDatum(amount),
1174 											   Int64GetDatum(scale)));
1175 
1176 	PG_RETURN_CASH(result);
1177 }
1178