summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaymond Hettinger <python@rcn.com>2008-06-09 06:54:45 (GMT)
committerRaymond Hettinger <python@rcn.com>2008-06-09 06:54:45 (GMT)
commitecbdd2e9b0a5af20a2b8784ac91338739b99ce3d (patch)
tree1cc2a450f85ce75d3c9a13741a755d44640a6821
parentdd96db63f689e2f0d8ae5a1436b3b3395eec7de5 (diff)
downloadcpython-ecbdd2e9b0a5af20a2b8784ac91338739b99ce3d.zip
cpython-ecbdd2e9b0a5af20a2b8784ac91338739b99ce3d.tar.gz
cpython-ecbdd2e9b0a5af20a2b8784ac91338739b99ce3d.tar.bz2
Issue #2138: Add math.factorial().
-rw-r--r--Doc/library/math.rst4
-rw-r--r--Lib/test/test_math.py15
-rw-r--r--Misc/NEWS2
-rw-r--r--Modules/mathmodule.c50
4 files changed, 71 insertions, 0 deletions
diff --git a/Doc/library/math.rst b/Doc/library/math.rst
index b98f164..d6e0205 100644
--- a/Doc/library/math.rst
+++ b/Doc/library/math.rst
@@ -42,6 +42,10 @@ Number-theoretic and representation functions:
Return the absolute value of *x*.
+.. function:: factorial(x)
+
+ Return *x* factorial. Raises :exc:`ValueError` if *x* is not intergral or
+ is negative.
.. function:: floor(x)
diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py
index 426ca7b..3123954 100644
--- a/Lib/test/test_math.py
+++ b/Lib/test/test_math.py
@@ -6,6 +6,7 @@ import unittest
import math
import os
import sys
+import random
eps = 1E-05
NAN = float('nan')
@@ -277,6 +278,20 @@ class MathTests(unittest.TestCase):
self.ftest('fabs(0)', math.fabs(0), 0)
self.ftest('fabs(1)', math.fabs(1), 1)
+ def testFactorial(self):
+ def fact(n):
+ result = 1
+ for i in range(1, int(n)+1):
+ result *= i
+ return result
+ values = range(10) + [50, 100, 500]
+ random.shuffle(values)
+ for x in range(10):
+ for cast in (int, long, float):
+ self.assertEqual(math.factorial(cast(x)), fact(x), (x, fact(x), math.factorial(x)))
+ self.assertRaises(ValueError, math.factorial, -1)
+ self.assertRaises(ValueError, math.factorial, math.pi)
+
def testFloor(self):
self.assertRaises(TypeError, math.floor)
# These types will be int in py3k.
diff --git a/Misc/NEWS b/Misc/NEWS
index b5afbcc..4337e5d 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -40,6 +40,8 @@ Core and Builtins
Extension Modules
-----------------
+- Issue #2138: Add factorial() the math module.
+
- The heapq module does comparisons using LT instead of LE. This
makes its implementation match that used by list.sort().
diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c
index 5520ca9..7ace997 100644
--- a/Modules/mathmodule.c
+++ b/Modules/mathmodule.c
@@ -512,6 +512,55 @@ PyDoc_STRVAR(math_sum_doc,
Return an accurate floating point sum of values in the iterable.\n\
Assumes IEEE-754 floating point arithmetic.");
+
+static PyObject *
+math_factorial(PyObject *self, PyObject *arg)
+{
+ long i, x;
+ PyObject *result, *iobj, *newresult;
+
+ if (PyFloat_Check(arg)) {
+ double dx = PyFloat_AS_DOUBLE((PyFloatObject *)arg);
+ if (dx != floor(dx)) {
+ PyErr_SetString(PyExc_ValueError,
+ "factorial() only accepts integral values");
+ return NULL;
+ }
+ }
+
+ x = PyInt_AsLong(arg);
+ if (x == -1 && PyErr_Occurred())
+ return NULL;
+ if (x < 0) {
+ PyErr_SetString(PyExc_ValueError,
+ "factorial() not defined for negative values");
+ return NULL;
+ }
+
+ result = (PyObject *)PyInt_FromLong(1);
+ if (result == NULL)
+ return NULL;
+ for (i=1 ; i<=x ; i++) {
+ iobj = (PyObject *)PyInt_FromLong(i);
+ if (iobj == NULL)
+ goto error;
+ newresult = PyNumber_Multiply(result, iobj);
+ Py_DECREF(iobj);
+ if (newresult == NULL)
+ goto error;
+ Py_DECREF(result);
+ result = newresult;
+ }
+ return result;
+
+error:
+ Py_DECREF(result);
+ Py_XDECREF(iobj);
+ return NULL;
+}
+
+PyDoc_STRVAR(math_factorial_doc, "Return n!");
+
static PyObject *
math_trunc(PyObject *self, PyObject *number)
{
@@ -949,6 +998,7 @@ static PyMethodDef math_methods[] = {
{"degrees", math_degrees, METH_O, math_degrees_doc},
{"exp", math_exp, METH_O, math_exp_doc},
{"fabs", math_fabs, METH_O, math_fabs_doc},
+ {"factorial", math_factorial, METH_O, math_factorial_doc},
{"floor", math_floor, METH_O, math_floor_doc},
{"fmod", math_fmod, METH_VARARGS, math_fmod_doc},
{"frexp", math_frexp, METH_O, math_frexp_doc},