Attached is a patch implementing factorial(), returning numeric. Points to
note:
1) arttype is numeric. I thought this was the best way of allowing
arbitarily large factorials, even though factorial(2^63) is a large
number. Happy to change to integers if this is overkill.
2) since we're accepting numeric arguments, the patch tests for floats. If
a numeric is passed with non-zero decimal portion, an error is raised
since (from memory) they are undefined.
3) I have not removed factorial([int2|int4|int8]), not their operator
counterparts since I didn't know what people would want done with these.
4) I haven't added any documentation but am happy to once I know if people
want int or numeric arguments.
Thanks,
Gavin
Index: src/backend/utils/adt/numeric.c
===================================================================
RCS file: /usr/local/cvsroot/pgsql-server/src/backend/utils/adt/numeric.c,v
retrieving revision 1.62
diff -2 -c -r1.62 numeric.c
*** src/backend/utils/adt/numeric.c 3 Jul 2003 19:41:47 -0000 1.62
--- src/backend/utils/adt/numeric.c 1 Aug 2003 14:48:23 -0000
***************
*** 1312,1315 ****
--- 1312,1429 ----
*/
+ /*
+ * numeric_fac()
+ * Computer factorial
+ */
+
+ Datum
+ numeric_fac(PG_FUNCTION_ARGS)
+ {
+
+ Numeric num1 = PG_GETARG_NUMERIC(0);
+ NumericVar count;
+ NumericVar fact;
+ NumericVar one;
+ NumericVar zerovar;
+ NumericVar result;
+ NumericVar tmp;
+ Numeric res;
+ int ret;
+ bool firstcall = true;
+
+ /*
+ * Handle NaN
+ */
+ if (NUMERIC_IS_NAN(num1))
+ PG_RETURN_NUMERIC(make_result(&const_nan));
+
+ init_var(&fact);
+ init_var(&one);
+ set_var_from_num(num1, &fact);
+ int8_to_numericvar((int64)1, &one);
+
+ ret = cmp_var(&fact, &one);
+
+ /* if <= 1, return(1) */
+
+ if(ret < 1) {
+ res = make_result(&one);
+ free_var(&fact);
+ free_var(&one);
+ PG_RETURN_NUMERIC(res);
+ }
+
+ init_var(&tmp);
+ set_var_from_num(num1, &fact);
+
+ /*
+ * n.x! is undefined, truncate factorial, and compare
+ * to original
+ */
+
+ set_var_from_var(&fact, &tmp);
+ trunc_var(&tmp,0);
+ ret = cmp_var(&fact, &tmp);
+ if(ret != 0) {
+ elog(ERROR,"Factorials can only be calculated on natural numbers");
+ }
+
+ /*
+ * Truncate factorial anyway, since n.0000 might have been passed
+ */
+
+ trunc_var(&fact, 0);
+
+ init_var(&count);
+ init_var(&result);
+ init_var(&zerovar);
+ zero_var(&zerovar);
+
+
+ int8_to_numericvar((int64)1, &one);
+ set_var_from_var(&one, &count);
+
+ /*
+ * Interate through N ... 1, multipling `count' by `fact' each time
+ */
+
+ while(1) {
+ if(!firstcall) {
+ free_var(&count);
+ init_var(&count);
+ set_var_from_var(&result,&count);
+
+ free_var(&result);
+ init_var(&result);
+
+ sub_var(&fact, &one, &result);
+ free_var(&fact);
+ init_var(&fact);
+ set_var_from_var(&result,&fact);
+
+ ret = cmp_var(&fact, &zerovar);
+ if(ret == 0) {
+ break;
+ }
+
+ free_var(&result);
+ init_var(&result);
+ } else {
+ firstcall = false;
+ }
+ mul_var(&count, &fact, &result, count.dscale + fact.dscale);
+ }
+
+ res = make_result(&count);
+
+ free_var(&count);
+ free_var(&fact);
+ free_var(&result);
+ free_var(&one);
+ free_var(&zerovar);
+
+ PG_RETURN_NUMERIC(res);
+ }
+
/*
Index: src/include/catalog/pg_operator.h
===================================================================
RCS file: /usr/local/cvsroot/pgsql-server/src/include/catalog/pg_operator.h,v
retrieving revision 1.118
diff -2 -c -r1.118 pg_operator.h
*** src/include/catalog/pg_operator.h 27 Jun 2003 00:33:25 -0000 1.118
--- src/include/catalog/pg_operator.h 1 Aug 2003 14:03:54 -0000
***************
*** 123,126 ****
--- 123,128 ----
DATA(insert OID = 352 ( "=" PGNSP PGUID b t 28 28 16 352 0
0 0 0 0 xideq eqsel eqjoinsel ));
DATA(insert OID = 353 ( "=" PGNSP PGUID b f 28 23 16 0
0 0 0 0 0 xideqint4 eqsel eqjoinsel ));
+ DATA(insert OID = 380 ( "!" PGNSP PGUID r f 1700 0 1700 0 0 0 0 0
0 numeric_fac - - ));
+ DATA(insert OID = 381 ( "!!" PGNSP PGUID r f 1700 0 1700 0 0 0 0 0
0 numeric_fac - - ));
DATA(insert OID = 385 ( "=" PGNSP PGUID b t 29 29 16 385 0
0 0 0 0 cideq eqsel eqjoinsel ));
DATA(insert OID = 386 ( "=" PGNSP PGUID b t 22 22 16 386 0
0 0 0 0 int2vectoreq eqsel eqjoinsel ));
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /usr/local/cvsroot/pgsql-server/src/include/catalog/pg_proc.h,v
retrieving revision 1.309
diff -2 -c -r1.309 pg_proc.h
*** src/include/catalog/pg_proc.h 1 Jul 2003 00:04:38 -0000 1.309
--- src/include/catalog/pg_proc.h 1 Aug 2003 14:42:12 -0000
***************
*** 232,236 ****
DATA(insert OID = 110 ( unknownout PGNSP PGUID 12 f f t f i 1 2275 "705"
unknownout - _null_ ));
DESCR("I/O");
!
DATA(insert OID = 112 ( text PGNSP PGUID 12 f f t f i 1 25 "23"
int4_text - _null_ ));
DESCR("convert int4 to text");
--- 232,236 ----
DATA(insert OID = 110 ( unknownout PGNSP PGUID 12 f f t f i 1 2275 "705"
unknownout - _null_ ));
DESCR("I/O");
! DATA(insert OID = 111 ( numeric_fac PGNSP PGUID 12 f f t f i 1 1700 "1700"
numeric_fac - _null_ ));
DATA(insert OID = 112 ( text PGNSP PGUID 12 f f t f i 1 25 "23"
int4_text - _null_ ));
DESCR("convert int4 to text");
***************
*** 1758,1761 ****
--- 1758,1763 ----
DESCR("factorial");
DATA(insert OID = 1393 ( factorial PGNSP PGUID 12 f f t f i 1 20 "20"
int8fac - _null_ ));
+ DESCR("factorial");
+ DATA(insert OID = 1376 ( factorial PGNSP PGUID 12 f f t f i 1 1700 "1700"
numeric_fac - _null_ ));
DESCR("factorial");
DATA(insert OID = 1394 ( abs PGNSP PGUID 12 f f t f i 1 700
"700" float4abs - _null_ ));
Index: src/include/utils/builtins.h
===================================================================
RCS file: /usr/local/cvsroot/pgsql-server/src/include/utils/builtins.h,v
retrieving revision 1.223
diff -2 -c -r1.223 builtins.h
*** src/include/utils/builtins.h 27 Jun 2003 00:33:26 -0000 1.223
--- src/include/utils/builtins.h 31 Jul 2003 18:21:12 -0000
***************
*** 705,708 ****
--- 705,709 ----
extern Datum numeric_smaller(PG_FUNCTION_ARGS);
extern Datum numeric_larger(PG_FUNCTION_ARGS);
+ extern Datum numeric_fac(PG_FUNCTION_ARGS);
extern Datum numeric_sqrt(PG_FUNCTION_ARGS);
extern Datum numeric_exp(PG_FUNCTION_ARGS);
---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to [EMAIL PROTECTED] so that your
message can get through to the mailing list cleanly