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

Reply via email to