Issue #2138: Add math.factorial().
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 @@
 
    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 math
 import os
 import sys
+import random
 
 eps = 1E-05
 NAN = float('nan')
@@ -277,6 +278,20 @@
         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 @@
 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 @@
 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 @@
 	{"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},