diff --git a/ChangeLog b/ChangeLog
index 451d45f339fe55c818e71c669a5b5bbbc1361719_Q2hhbmdlTG9n..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_Q2hhbmdlTG9n 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Thu Jan 25 12:13:04 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
+
+	* functions.[ch]: Bjorn Reese <breese@mail1.stofanet.dk> provided
+	  number formatting !!!
+	* acconfig.h config.h.in configure.in libxslt/Makefile.am
+	  tests/Makefile.am; added testing for mathematical functions,
+	  fixed make test(s)
+	* FEATURES: updated
+
 Wed Jan 24 16:59:05 CET 2001 Daniel Veillard <Daniel.Veillard@imag.fr>
 
 	* libxslt/xsltInternals.h libxslt/pattern.c: fixed problems
diff --git a/FEATURES b/FEATURES
index 451d45f339fe55c818e71c669a5b5bbbc1361719_RkVBVFVSRVM=..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_RkVBVFVSRVM= 100644
--- a/FEATURES
+++ b/FEATURES
@@ -203,7 +203,7 @@
 
 NO		    node-set document(object, node-set?)
 NO		    node-set key(string, object)
-NO		    string format-number(number, string, string?)
+YES		    string format-number(number, string, string?)
 NO		    node-set current() 
 NO		    string unparsed-entity-uri(string)
 NO		    string generate-id(node-set?)
diff --git a/acconfig.h b/acconfig.h
new file mode 100644
index 0000000000000000000000000000000000000000..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_YWNjb25maWcuaA==
--- /dev/null
+++ b/acconfig.h
@@ -0,0 +1,5 @@
+#undef HAVE_ISINF
+#undef HAVE_ISNAN
+#undef HAVE_POW
+#undef HAVE_FLOOR
+#undef HAVE_FABS
diff --git a/config.h.in b/config.h.in
index 451d45f339fe55c818e71c669a5b5bbbc1361719_Y29uZmlnLmguaW4=..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_Y29uZmlnLmguaW4= 100644
--- a/config.h.in
+++ b/config.h.in
@@ -5,3 +5,24 @@
 
 /* Define if you have the ANSI C header files.  */
 #undef STDC_HEADERS
+
+#undef HAVE_ISINF
+#undef HAVE_ISNAN
+#undef HAVE_POW
+#undef HAVE_FLOOR
+#undef HAVE_FABS
+
+/* Define if you have the <float.h> header file.  */
+#undef HAVE_FLOAT_H
+
+/* Define if you have the <fp_class.h> header file.  */
+#undef HAVE_FP_CLASS_H
+
+/* Define if you have the <ieeefp.h> header file.  */
+#undef HAVE_IEEEFP_H
+
+/* Define if you have the <math.h> header file.  */
+#undef HAVE_MATH_H
+
+/* Define if you have the <nan.h> header file.  */
+#undef HAVE_NAN_H
diff --git a/configure.in b/configure.in
index 451d45f339fe55c818e71c669a5b5bbbc1361719_Y29uZmlndXJlLmlu..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_Y29uZmlndXJlLmlu 100644
--- a/configure.in
+++ b/configure.in
@@ -8,6 +8,37 @@
 AM_MAINTAINER_MODE
 
 dnl
+dnl Check the environment
+dnl
+
+AC_ISC_POSIX
+AC_PROG_CC
+AC_STDC_HEADERS
+AC_ARG_PROGRAM
+AM_PROG_LIBTOOL
+
+dnl
+dnl Math detection
+dnl
+
+AC_CHECK_HEADERS(ieeefp.h nan.h math.h fp_class.h float.h)
+AC_CHECK_FUNC(isnan, , AC_CHECK_LIB(m, isnan,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_ISNAN)]))
+
+AC_CHECK_FUNC(isinf, , AC_CHECK_LIB(m, isinf,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_ISINF)]))
+
+AC_CHECK_FUNC(pow, , AC_CHECK_LIB(m, pow,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_POW)]))
+
+AC_CHECK_FUNC(floor, , AC_CHECK_LIB(m, pow,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_FLOOR)]))
+
+AC_CHECK_FUNC(fabs, , AC_CHECK_LIB(m, pow,
+  [M_LIBS="-lm"; AC_DEFINE(HAVE_FABS)]))
+
+
+dnl
 dnl Debug for DV
 dnl
 if test "${LOGNAME}" = "veillard" -a "`pwd`" = "/u/veillard/XSLT" ; then
@@ -49,16 +80,6 @@
 )
 
 
-dnl
-dnl Check the environment
-dnl
-
-AC_ISC_POSIX
-AC_PROG_CC
-AC_STDC_HEADERS
-AC_ARG_PROGRAM
-AM_PROG_LIBTOOL
-
 dnl No internationalization (yet ?)
 dnl 
 dnl ALL_LINGUAS="it ko fr de es no ga sv pt ja fi cs"
@@ -110,7 +131,8 @@
 
 XSLT_LIBDIR='-L${libdir}'
 XSLT_INCLUDEDIR='-I${includedir}'
-XSLT_LIBS="-lxslt $LIBXML_LIBS"
+EXTRA_LIBS="$LIBXML_LIBS $M_LIBS"
+XSLT_LIBS="-lxslt $LIBXML_LIBS $M_LIBS"
 
 AC_SUBST(XSLT_LIBDIR)
 AC_SUBST(XSLT_INCLUDEDIR)
@@ -114,6 +136,7 @@
 
 AC_SUBST(XSLT_LIBDIR)
 AC_SUBST(XSLT_INCLUDEDIR)
+AC_SUBST(EXTRA_LIBS)
 AC_SUBST(XSLT_LIBS)
 
 AC_OUTPUT([
diff --git a/libxslt/Makefile.am b/libxslt/Makefile.am
index 451d45f339fe55c818e71c669a5b5bbbc1361719_bGlieHNsdC9NYWtlZmlsZS5hbQ==..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_bGlieHNsdC9NYWtlZmlsZS5hbQ== 100644
--- a/libxslt/Makefile.am
+++ b/libxslt/Makefile.am
@@ -32,7 +32,7 @@
 bin_PROGRAMS = xsltproc
 
 DEPS = $(top_builddir)/libxslt/libxslt.la
-LDADDS = -L. $(top_builddir)/libxslt/libxslt.la $(LIBXML_LIBS)
+LDADDS = -L. $(top_builddir)/libxslt/libxslt.la $(EXTRA_LIBS)
 
 xsltproc_SOURCES = xsltproc.c
 xsltproc_LDFLAGS =
diff --git a/libxslt/functions.c b/libxslt/functions.c
index 451d45f339fe55c818e71c669a5b5bbbc1361719_bGlieHNsdC9mdW5jdGlvbnMuYw==..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_bGlieHNsdC9mdW5jdGlvbnMuYw== 100644
--- a/libxslt/functions.c
+++ b/libxslt/functions.c
@@ -7,9 +7,10 @@
  * See Copyright for the status of this software.
  *
  * Daniel.Veillard@imag.fr
+ * Bjorn Reese <breese@mail1.stofanet.dk> for number formatting
  */
 
 #include "xsltconfig.h"
 
 #include <string.h>
 
@@ -10,7 +11,26 @@
  */
 
 #include "xsltconfig.h"
 
 #include <string.h>
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+#ifdef HAVE_FLOAT_H
+#include <float.h>
+#endif
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+#ifdef HAVE_NAN_H
+#include <nan.h>
+#endif
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
 #include <libxml/xmlmemory.h>
@@ -16,4 +36,5 @@
 #include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
 #include <libxml/tree.h>
 #include <libxml/valid.h>
 #include <libxml/hash.h>
@@ -28,6 +49,50 @@
 
 #define DEBUG_FUNCTION
 
+#ifndef FALSE
+# define FALSE (0 == 1)
+# define TRUE (1 == 1)
+#endif
+
+#define DIGIT_LIST "0123456789"
+#define SYMBOL_QUOTE             ((xmlChar)'\'')
+#define SYMBOL_PATTERN_SEPARATOR ((xmlChar)';')
+#define SYMBOL_ZERO_DIGIT        ((xmlChar)'#')
+#define SYMBOL_DIGIT             ((xmlChar)'0')
+#define SYMBOL_DECIMAL_POINT     ((xmlChar)'.')
+#define SYMBOL_GROUPING          ((xmlChar)',')
+#define SYMBOL_MINUS             ((xmlChar)'-')
+#define SYMBOL_PERCENT           ((xmlChar)'%')
+#define SYMBOL_PERMILLE          ((xmlChar)'?')
+
+static struct _xsltDecimalFormat globalDecimalFormat;
+
+/************************************************************************
+ *									*
+ *			Utility functions				*
+ *									*
+ ************************************************************************/
+
+#ifndef isnan
+static int
+isnan(volatile double number)
+{
+    return (!(number < 0.0 || number > 0.0) && (number != 0.0));
+}
+#endif
+
+#ifndef isinf
+static int
+isinf(double number)
+{
+# ifdef HUGE_VAL
+    return ((number == HUGE_VAL) ? 1 : ((number == -HUGE_VAL) ? -1 : 0));
+# else
+    return FALSE;
+# endif
+}
+#endif
+
 
 /************************************************************************
  *									*
@@ -82,4 +147,212 @@
  * Implement the format-number() XSLT function
  *   string format-number(number, string, string?)
  */
+/*
+ * JDK 1.1 DecimalFormat class:
+ *
+ * http://java.sun.com/products/jdk/1.1/docs/api/java.text.DecimalFormat.html
+ *
+ * Structure:
+ *
+ *   pattern    := subpattern{;subpattern}
+ *   subpattern := {prefix}integer{.fraction}{suffix}
+ *   prefix     := '\\u0000'..'\\uFFFD' - specialCharacters
+ *   suffix     := '\\u0000'..'\\uFFFD' - specialCharacters
+ *   integer    := '#'* '0'* '0'
+ *   fraction   := '0'* '#'*
+ *
+ *   Notation:
+ *    X*       0 or more instances of X
+ *    (X | Y)  either X or Y.
+ *    X..Y     any character from X up to Y, inclusive.
+ *    S - T    characters in S, except those in T
+ *
+ * Special Characters:
+ *
+ *   Symbol Meaning
+ *   0      a digit
+ *   #      a digit, zero shows as absent
+ *   .      placeholder for decimal separator
+ *   ,      placeholder for grouping separator.
+ *   ;      separates formats.
+ *   -      default negative prefix.
+ *   %      multiply by 100 and show as percentage
+ *   ?      multiply by 1000 and show as per mille
+ *   X      any other characters can be used in the prefix or suffix
+ *   '      used to quote special characters in a prefix or suffix.
+ */
+/* TODO
+ *
+ *  The JDK description does not tell where percent and permille may
+ *  and may not appear within the format string.
+ *
+ *  Error handling.
+ *
+ *  Inf and NaN not tested.
+ */
+#define IS_SPECIAL(self,letter) \
+    ((letter == self->zeroDigit) || \
+     (letter == self->digit) || \
+     (letter == self->decimalPoint) || \
+     (letter == self->grouping) || \
+     (letter == self->minusSign) || \
+     (letter == self->percent) || \
+     (letter == self->permille)) \
+
+static xmlChar *
+PrivateDecimalFormat(xsltDecimalFormatPtr self,
+		     xmlChar *format,
+		     double number)
+{
+    xmlChar *the_format;
+    xmlBufferPtr buffer;
+    xmlChar *result;
+    char digit_buffer[2];
+    int use_minus;
+    int i, j;
+    int length;
+    int group;
+    int integer_digits = 0;
+    int integer_zeroes = 0;
+    int fraction_digits = 0;
+    int fraction_zeroes = 0;
+    int decimal_point;
+    double divisor;
+    int digit;
+
+    buffer = xmlBufferCreate();
+
+    /* Find positive or negative template */
+    the_format = (xmlChar *)xmlStrchr(format,
+				      self->patternSeparator);
+    if ((the_format != NULL) && (number < 0.0)) {
+	/* Use negative template */
+	the_format++;
+	use_minus = FALSE;
+    } else {
+	/* Use positive template */
+	if (the_format)
+	    the_format[0] = 0;
+	the_format = format;
+	use_minus = (number < 0.0) ? TRUE : FALSE;
+    }
+  
+    /* Prefix */
+    length = xmlStrlen(the_format);
+    for (i = 0; i < length; i++) {
+	if (IS_SPECIAL(self, the_format[i])) {
+	    break; /* for */
+	} else {
+	    if (the_format[i] == SYMBOL_QUOTE) {
+		/* Quote character */
+		i++;
+	    }
+	    xmlBufferAdd(buffer, &the_format[i], 1);
+	}
+    }
+
+    if (isinf(number)) {
+	xmlBufferCat(buffer, self->infinity);
+	/* Skip until suffix */
+	for ( ; i < length; i++) {
+	    if (! IS_SPECIAL(self, the_format[i]))
+		break; /* for */
+	}
+    } else if (isnan(number)) {
+	xmlBufferCat(buffer, self->noNumber);
+	/* Skip until suffix */
+	for ( ; i < length; i++) {
+	    if (! IS_SPECIAL(self, the_format[i]))
+		break; /* for */
+	}
+    } else {
+	
+	/* Parse the number part of the format string */
+	decimal_point = FALSE;
+	group = 0;
+	for ( ; i < length; i++) {
+	    
+	    if (the_format[i] == self->digit) {
+		if (decimal_point) {
+		    if (fraction_zeroes > 0)
+			; /* Error in format */
+		    fraction_digits++;
+		} else {
+		    integer_digits++;
+		    group++;
+		}
+		
+	    } else if (the_format[i] == self->zeroDigit) {
+		if (decimal_point)
+		    fraction_zeroes++;
+		else {
+		    if (integer_digits > 0)
+			; /* Error in format */
+		    integer_zeroes++;
+		    group++;
+		}
+		
+	    } else if (the_format[i] == self->grouping) {
+		if (decimal_point)
+		    ; /* Error in format */
+		group = 0;
+		
+	    } else if (the_format[i] == self->decimalPoint) {
+		if (decimal_point)
+		    ; /* Error in format */
+		decimal_point = TRUE;
+		
+	    } else
+		break;
+	}
+	
+	/* Format the number */
+
+	if (use_minus)
+	    xmlBufferAdd(buffer, &(self->minusSign), 1);
+
+	number = fabs(number);
+	number = floor(0.5 + number * pow(10.0, (double)(fraction_digits + fraction_zeroes)));
+	
+	/* Integer part */
+	digit_buffer[1] = (char)0;
+	j = integer_digits + integer_zeroes;
+	divisor = pow(10.0, (double)(integer_digits + integer_zeroes + fraction_digits + fraction_zeroes - 1));
+	for ( ; j > 0; j--) {
+	    digit = (int)(number / divisor);
+	    number -= (double)digit * divisor;
+	    divisor /= 10.0;
+	    if ((digit > 0) || (j <= integer_digits)) {
+		digit_buffer[0] = DIGIT_LIST[digit];
+		xmlBufferCCat(buffer, digit_buffer);
+	    }
+	}
+	
+	if (decimal_point)
+	    xmlBufferAdd(buffer, &(self->decimalPoint), 1);
+
+	/* Fraction part */
+	for (j = fraction_digits + fraction_zeroes; j > 0; j--) {
+	    digit = (int)(number / divisor);
+	    number -= (double)digit * divisor;
+	    divisor /= 10.0;
+	    if ((digit > 0) || (j > fraction_zeroes)) {
+		digit_buffer[0] = DIGIT_LIST[digit];
+		xmlBufferCCat(buffer, digit_buffer);
+	    }
+	}
+    }
+
+    /* Suffix */
+    for ( ; i < length; i++) {
+	if (the_format[i] == SYMBOL_QUOTE)
+	    i++;
+	xmlBufferAdd(buffer, &the_format[i], 1);
+    }
+    
+    result = xmlStrdup(xmlBufferContent(buffer));
+    xmlBufferFree(buffer);
+    return result;
+}
+
 void
@@ -85,6 +358,34 @@
 void
-xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs){
-    TODO /* function */
+xsltFormatNumberFunction(xmlXPathParserContextPtr ctxt, int nargs)
+{
+    xmlXPathObjectPtr numberObj = NULL;
+    xmlXPathObjectPtr formatObj = NULL;
+    xmlXPathObjectPtr decimalObj = NULL;
+    
+    switch (nargs) {
+    case 3:
+	CAST_TO_STRING;
+	decimalObj = valuePop(ctxt);
+	globalDecimalFormat.decimalPoint = decimalObj->stringval[0]; /* hack */
+	/* Intentional fall-through */
+    case 2:
+	CAST_TO_STRING;
+	formatObj = valuePop(ctxt);
+	CAST_TO_NUMBER;
+	numberObj = valuePop(ctxt);
+	break;
+    default:
+	XP_ERROR(XPATH_INVALID_ARITY);
+    }
+    
+    valuePush(ctxt,
+	      xmlXPathNewString(PrivateDecimalFormat(&globalDecimalFormat,
+						     formatObj->stringval,
+						     numberObj->floatval)));
+    
+    xmlXPathFreeObject(numberObj);
+    xmlXPathFreeObject(formatObj);
+    xmlXPathFreeObject(decimalObj);
 }
 
 /**
diff --git a/libxslt/functions.h b/libxslt/functions.h
index 451d45f339fe55c818e71c669a5b5bbbc1361719_bGlieHNsdC9mdW5jdGlvbnMuaA==..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_bGlieHNsdC9mdW5jdGlvbnMuaA== 100644
--- a/libxslt/functions.h
+++ b/libxslt/functions.h
@@ -4,6 +4,7 @@
  * See Copyright for the status of this software.
  *
  * Daniel.Veillard@imag.fr
+ * Bjorn Reese <breese@mail1.stofanet.dk> for number formatting
  */
 
 #ifndef __XML_XSLT_FUNCTIONS_H__
@@ -18,6 +19,25 @@
 #endif
 
 /*
+ * Data structure of decimal-format
+ */
+typedef struct _xsltDecimalFormat {
+    /* Used for interpretation of pattern */
+    xmlChar digit;
+    xmlChar patternSeparator;
+    /* May appear in result */
+    xmlChar minusSign;
+    xmlChar *infinity;
+    xmlChar *noNumber;
+    /* Used for interpretation of pattern and may appear in result */
+    xmlChar decimalPoint;
+    xmlChar grouping;
+    xmlChar percent;
+    xmlChar permille;
+    xmlChar zeroDigit;
+} xsltDecimalFormat, *xsltDecimalFormatPtr;
+
+/*
  * Interfaces for the functions implementations
  */
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 451d45f339fe55c818e71c669a5b5bbbc1361719_dGVzdHMvTWFrZWZpbGUuYW0=..1d09e1fb781f3f2f0e80b1e5cc7379aded1d0c70_dGVzdHMvTWFrZWZpbGUuYW0= 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -2,3 +2,4 @@
 
 SUBDIRS=REC1 REC2
 
+test tests: all