You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2022/10/19 20:38:36 UTC

[commons-numbers] branch master updated: NUMBERS-70: Initial user guide

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git


The following commit(s) were added to refs/heads/master by this push:
     new 07443607 NUMBERS-70: Initial user guide
07443607 is described below

commit 07443607d94fa3caf17dba8e9396c3907115c178
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Mon Oct 17 16:00:29 2022 +0100

    NUMBERS-70: Initial user guide
    
    Add test classes to verify the user guide examples.
---
 .../commons/numbers/angle/UserGuideTest.java       |  65 ++
 .../commons/numbers/arrays/UserGuideTest.java      |  59 ++
 .../numbers/combinatorics/UserGuideTest.java       |  94 ++
 .../commons/numbers/complex/UserGuideTest.java     |  59 ++
 .../apache/commons/numbers/core/UserGuideTest.java | 132 +++
 .../commons/numbers/field/UserGuideTest.java       |  34 +
 .../fraction/GeneralizedContinuedFractionTest.java |   4 +-
 .../commons/numbers/fraction/UserGuideTest.java    | 156 ++++
 .../commons/numbers/gamma/UserGuideTest.java       |  43 +
 .../commons/numbers/primes/UserGuideTest.java      |  44 +
 .../commons/numbers/quaternion/SlerpTest.java      |   6 +-
 .../commons/numbers/quaternion/UserGuideTest.java  |  84 ++
 .../commons/numbers/rootfinder/UserGuideTest.java  |  45 +
 src/site/apt/userguide/index.apt                   | 991 +++++++++++++++++++++
 src/site/site.xml                                  |  29 +-
 src/site/xdoc/userguide/core.xml                   |  38 -
 src/site/xdoc/userguide/index.xml                  |  35 -
 src/site/xdoc/userguide/xdoc.xsl                   |  68 --
 18 files changed, 1832 insertions(+), 154 deletions(-)

diff --git a/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/UserGuideTest.java b/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/UserGuideTest.java
new file mode 100644
index 00000000..5126fc11
--- /dev/null
+++ b/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/UserGuideTest.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.angle;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testAngle1() {
+        Angle.Deg a = Angle.Turn.of(0.5).toDeg();
+        Angle.Rad b = a.toRad();
+        Angle.Turn c = b.toTurn();
+        Assertions.assertEquals(0.5, c.getAsDouble());
+    }
+
+    @Test
+    void testAngleNormalizer1() {
+        double angle = Angle.Deg.normalizer(-180).applyAsDouble(270);
+        Assertions.assertEquals(-90.0, angle);
+    }
+
+    @Test
+    void testReduce1() {
+        Reduce reduce = new Reduce(0, 24);
+        double hours1 = reduce.applyAsDouble(173.5);
+        double hours2 = reduce.applyAsDouble(23.5);
+        double hours3 = reduce.applyAsDouble(-10);
+        Assertions.assertEquals(5.5, hours1);
+        Assertions.assertEquals(23.5, hours2);
+        Assertions.assertEquals(14.0, hours3);
+    }
+
+    @Test
+    void testCosAngle1() {
+        double[] v1 = {1, 0};
+        double[] v2 = {0, 1};
+        double[] v3 = {7, 7};
+        double cosAngle1 = CosAngle.value(v1, v1);
+        double cosAngle2 = CosAngle.value(v1, v2);
+        double cosAngle3 = CosAngle.value(v1, v3);
+        Assertions.assertEquals(1, cosAngle1);
+        Assertions.assertEquals(0, cosAngle2);
+        Assertions.assertEquals(0.7071067811865476, cosAngle3);
+        Assertions.assertEquals(45.0, Math.toDegrees(Math.acos(cosAngle3)));
+    }
+}
diff --git a/commons-numbers-arrays/src/test/java/org/apache/commons/numbers/arrays/UserGuideTest.java b/commons-numbers-arrays/src/test/java/org/apache/commons/numbers/arrays/UserGuideTest.java
new file mode 100644
index 00000000..64c20f19
--- /dev/null
+++ b/commons-numbers-arrays/src/test/java/org/apache/commons/numbers/arrays/UserGuideTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.arrays;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testSortInPlace1() {
+        double[] x = {3, 1, 2};
+        double[] y = {1, 2, 3};
+        double[] z = {0, 5, 7};
+        SortInPlace.ASCENDING.apply(x, y, z);
+        Assertions.assertArrayEquals(new double[] {1, 2, 3}, x);
+        Assertions.assertArrayEquals(new double[] {2, 3, 1}, y);
+        Assertions.assertArrayEquals(new double[] {5, 7, 0}, z);
+    }
+
+    @Test
+    void testMultidimensionalCounter1() {
+        MultidimensionalCounter c = MultidimensionalCounter.of(100, 50);
+        int size = c.getSize();
+        Assertions.assertEquals(5000, size);
+
+        int index = 233;
+        int[] indices1 = c.toMulti(index);
+        int[] indices2 = c.toMulti(index + 1);
+        Assertions.assertArrayEquals(new int[] {4, 33}, indices1);
+        Assertions.assertArrayEquals(new int[] {4, 34}, indices2);
+
+        int index1 = c.toUni(4, 33); // varargs
+        int index2 = c.toUni(indices1);
+        Assertions.assertEquals(index, index1);
+        Assertions.assertEquals(index, index2);
+
+        Assertions.assertArrayEquals(new int[] {0, 49}, c.toMulti(49));
+        Assertions.assertArrayEquals(new int[] {1, 0}, c.toMulti(50));
+        Assertions.assertArrayEquals(new int[] {99, 49}, c.toMulti(4999));
+    }
+}
diff --git a/commons-numbers-combinatorics/src/test/java/org/apache/commons/numbers/combinatorics/UserGuideTest.java b/commons-numbers-combinatorics/src/test/java/org/apache/commons/numbers/combinatorics/UserGuideTest.java
new file mode 100644
index 00000000..38ef9c6d
--- /dev/null
+++ b/commons-numbers-combinatorics/src/test/java/org/apache/commons/numbers/combinatorics/UserGuideTest.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.combinatorics;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testBinomialCoefficient1() {
+        long   bc1 = BinomialCoefficient.value(66, 33);
+        double bc2 = BinomialCoefficientDouble.value(1029, 514);
+        double bc3 = LogBinomialCoefficient.value(152635712, 125636);
+        Assertions.assertEquals(7219428434016265740L, bc1);
+        // Test with relative tolerance.
+        // Expected values taken from the test suite.
+        Assertions.assertEquals(1.429820686498904e308, bc2, bc2 * 1e-15);
+        Assertions.assertEquals(1017897.199659759, bc3, bc3 * 1e-15);
+    }
+
+    @Test
+    void testFactorial1() {
+        long   f1 = Factorial.value(15);
+        double f2 = Factorial.doubleValue(170);
+        double f3 = LogFactorial.create().value(Integer.MAX_VALUE);
+        Assertions.assertEquals(1307674368000L, f1);
+        Assertions.assertEquals(7.257415615307999e306, f2);
+        // This value is taken using LogFactorial.
+        // It should be verified independently.
+        // Currently this asserts the value in the user guide is correct.
+        Assertions.assertEquals(4.3996705655378525e10, f3);
+    }
+
+    @Test
+    void testLogFactorial1() {
+        LogFactorial lf = LogFactorial.create().withCache(50);
+        // This is a compilation test. Just verify the object is created.
+        Assertions.assertNotNull(lf);
+    }
+
+    @Test
+    void testCombinations1() {
+        List<String> actual = new ArrayList<>();
+        Combinations.of(4, 2).iterator().forEachRemaining(c -> actual.add(Arrays.toString(c)));
+        Assertions.assertEquals(Arrays.asList(
+                "[0, 1]",
+                "[0, 2]",
+                "[1, 2]",
+                "[0, 3]",
+                "[1, 3]",
+                "[2, 3]"
+            ), actual);
+    }
+
+    @Test
+    void testCombinationsComparator1() {
+        List<int[]> list = Arrays.asList(new int[][] {
+            {3, 4, 5},
+            {3, 1, 5},
+            {3, 2, 5},
+            {4, 2, 4},
+        });
+        list.sort(Combinations.of(6, 3).comparator());
+        List<String> actual = new ArrayList<>();
+        list.forEach(c -> actual.add(Arrays.toString(c)));
+        Assertions.assertEquals(Arrays.asList(
+                "[4, 2, 4]",
+                "[3, 1, 5]",
+                "[3, 2, 5]",
+                "[3, 4, 5]"
+            ), actual);
+    }
+}
diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/UserGuideTest.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/UserGuideTest.java
new file mode 100644
index 00000000..72e0ab6d
--- /dev/null
+++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/UserGuideTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.complex;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testComplex1() {
+        double x = 3;
+        double y = 4;
+        Complex c1 = Complex.ofCartesian(x, y);
+        // c1 == x + iy
+
+        double rho = 1.23;
+        double theta = Math.PI / 2;
+        Complex c2 = Complex.ofPolar(rho, theta);
+
+        // This is a compilation test. Objects must be valid.
+        Assertions.assertNotNull(c1);
+        Assertions.assertNotNull(c2);
+    }
+
+    @Test
+    void testComplex2() {
+        Complex c1 = Complex.ofCartesian(3, 4);
+        Complex c2 = Complex.ofCartesian(5, 6);
+        Complex c3 = c1.multiply(c2).sqrt();
+
+        double magnitude = c3.abs();
+        double argument = c3.arg();
+
+        boolean finite = c3.isFinite();
+
+        // This is a compilation test. Objects must be valid.
+        Assertions.assertTrue(Double.isFinite(magnitude));
+        Assertions.assertTrue(Double.isFinite(argument));
+        Assertions.assertTrue(finite);
+    }
+}
diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/UserGuideTest.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/UserGuideTest.java
new file mode 100644
index 00000000..537c1a92
--- /dev/null
+++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/UserGuideTest.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.core;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testNorm1() {
+        double x = Norm.EUCLIDEAN.of(3, -4);
+        double y = Norm.MANHATTAN.of(3, -4, 5);
+        double z = Norm.MAXIMUM.of(new double[] {3, -4, 5, -6, -7, -8});
+        Assertions.assertEquals(5, x);
+        Assertions.assertEquals(12, y);
+        Assertions.assertEquals(8, z);
+
+        double big = Double.MAX_VALUE * 0.5;
+        double length = Norm.EUCLIDEAN.of(big, big, big);
+        double expected = Math.sqrt(0.5 * 0.5 * 3) * Double.MAX_VALUE;
+        Assertions.assertEquals(expected, length, Math.ulp(expected));
+    }
+
+    @Test
+    void testSum1() {
+        double sum1 = Sum.create().add(1)
+                                  .addProduct(3, 4)
+                                  .getAsDouble();
+        double sum2 = Sum.of(1).addProduct(3, 4)
+                               .getAsDouble();
+        double sum3 = Sum.ofProducts(new double[] {3, 4}, new double[] {5, 6})
+                         .getAsDouble();
+        Assertions.assertEquals(13, sum1);
+        Assertions.assertEquals(13, sum2);
+        Assertions.assertEquals(3 * 5 + 4 * 6, sum3);
+
+        Assertions.assertEquals(Double.NaN, Sum.of(1, 2, Double.NaN).getAsDouble());
+        Assertions.assertEquals(Double.NEGATIVE_INFINITY, Sum.of(1, 2, Double.NEGATIVE_INFINITY).getAsDouble());
+    }
+
+    @Test
+    void testSum2() {
+        double x1 = 1e100 + 1 - 2 - 1e100;
+        double x2 = Sum.of(1e100, 1, -2, -1e100).getAsDouble();
+        Assertions.assertEquals(0.0, x1);
+        Assertions.assertEquals(-1.0, x2);
+    }
+
+    @Test
+    void testPrecision1() {
+        // Default allows no numbers between
+        Assertions.assertTrue(Precision.equals(1000.0, 1000.0));
+        Assertions.assertTrue(Precision.equals(1000.0, 1000.0 + Math.ulp(1000.0)));
+        Assertions.assertFalse(Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0)));
+
+        // Absolute - tolerance is floating-point
+        Assertions.assertFalse(Precision.equals(1000.0, 1001.0));
+        Assertions.assertTrue(Precision.equals(1000.0, 1001.0, 1.0));
+        Assertions.assertTrue(Precision.equals(1000.0, 1000.0 + Math.ulp(1000.0), 0.0));
+
+        // ULP - tolerance is integer
+        Assertions.assertFalse(Precision.equals(1000.0, 1001.0));
+        Assertions.assertFalse(Precision.equals(1000.0, 1001.0, 1));
+        Assertions.assertFalse(Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0), 1));
+        Assertions.assertTrue(Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0), 2));
+        Assertions.assertFalse(Precision.equals(1000.0, 1000.0 + 3 * Math.ulp(1000.0), 2));
+
+        // Relative
+        Assertions.assertFalse(Precision.equalsWithRelativeTolerance(1000.0, 1001.0, 1e-6));
+        Assertions.assertTrue(Precision.equalsWithRelativeTolerance(1000.0, 1001.0, 1e-3));
+    }
+
+    @Test
+    void testPrecision2() {
+        Assertions.assertFalse(Precision.equals(Double.NaN, 1000.0));
+        Assertions.assertFalse(Precision.equals(Double.NaN, Double.NaN));
+        Assertions.assertTrue(Precision.equalsIncludingNaN(Double.NaN, Double.NaN));
+
+        Assertions.assertTrue(Precision.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY));
+        Assertions.assertTrue(Precision.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY));
+        Assertions.assertFalse(Precision.equals(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY));
+    }
+
+    @Test
+    void testPrecision3() {
+        Assertions.assertEquals(0, Precision.compareTo(100, 100, 0.0));
+        Assertions.assertEquals(0, Precision.compareTo(100, 101, 1.0));
+        Assertions.assertEquals(-1, Precision.compareTo(100, 102, 1.0));
+        Assertions.assertEquals(1, Precision.compareTo(102, 100, 1.0));
+    }
+
+    @Test
+    void testPrecision4() {
+        Precision.DoubleEquivalence eq = Precision.doubleEquivalenceOfEpsilon(1.0);
+        Assertions.assertFalse(eq.lt(100, 100));
+        Assertions.assertTrue(eq.lte(100, 100));
+        Assertions.assertTrue(eq.eq(100, 100));
+        Assertions.assertTrue(eq.gte(100, 100));
+        Assertions.assertFalse(eq.gt(100, 100));
+    }
+
+    @Test
+    void testPrecision5() {
+        Assertions.assertEquals(678.125, Precision.round(678.125, 4));
+        Assertions.assertEquals(678.125, Precision.round(678.125, 3));
+        Assertions.assertEquals(678.13, Precision.round(678.125, 2));
+        Assertions.assertEquals(678.1, Precision.round(678.125, 1));
+        Assertions.assertEquals(678.0, Precision.round(678.125, 0));
+        Assertions.assertEquals(680.0, Precision.round(678.125, -1));
+        Assertions.assertEquals(700.0, Precision.round(678.125, -2));
+
+        Assertions.assertEquals(0.10000000000000009, Precision.representableDelta(1.0, 0.1));
+    }
+}
diff --git a/commons-numbers-field/src/test/java/org/apache/commons/numbers/field/UserGuideTest.java b/commons-numbers-field/src/test/java/org/apache/commons/numbers/field/UserGuideTest.java
new file mode 100644
index 00000000..136c375d
--- /dev/null
+++ b/commons-numbers-field/src/test/java/org/apache/commons/numbers/field/UserGuideTest.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.field;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testField1() {
+        Field<FP64> field = FP64Field.get();
+        FP64 result = field.one().multiply(3).divide(field.one().multiply(4));
+        double value = result.doubleValue();
+        Assertions.assertEquals(0.75, value);
+    }
+}
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFractionTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFractionTest.java
index dd5fd5a9..ff22edf7 100644
--- a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFractionTest.java
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/GeneralizedContinuedFractionTest.java
@@ -525,7 +525,7 @@ class GeneralizedContinuedFractionTest {
      *                  5 - ...
      * </pre>
      */
-    private static class Tan implements Supplier<Coefficient> {
+    static class Tan implements Supplier<Coefficient> {
         private double a;
         private double b = -1;
 
@@ -567,7 +567,7 @@ class GeneralizedContinuedFractionTest {
      * @param coefficients b coefficients
      * @return the generator
      */
-    private static Supplier<Coefficient> simpleContinuedFraction(double... coefficients) {
+    static Supplier<Coefficient> simpleContinuedFraction(double... coefficients) {
         return new Supplier<Coefficient>() {
             /* iteration. */
             private int n;
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/UserGuideTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/UserGuideTest.java
new file mode 100644
index 00000000..b226f0e6
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/UserGuideTest.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.fraction;
+
+import java.util.function.Supplier;
+import org.apache.commons.numbers.fraction.GeneralizedContinuedFraction.Coefficient;
+import org.apache.commons.numbers.fraction.GeneralizedContinuedFractionTest.Tan;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testFraction1() {
+        int maxIterations = 100;
+
+        // tolerance
+        Fraction a = Fraction.from(0.6152, 0.02, maxIterations);
+        Fraction b = Fraction.from(0.6152, 1.0e-7, maxIterations);
+        Assertions.assertEquals(Fraction.of(3, 5), a);
+        Assertions.assertEquals(Fraction.of(769, 1250), b);
+        Assertions.assertEquals(0.6152, b.doubleValue(), "exact");
+
+        // max denominator
+        Fraction c = Fraction.from(0.6152, 9);
+        Fraction d = Fraction.from(0.6152, 9999);
+        Assertions.assertEquals(Fraction.of(3, 5), c);
+        Assertions.assertEquals(Fraction.of(769, 1250), d);
+
+        // largest possible value
+        Fraction e = Fraction.from(Math.pow(2, 31));
+        Assertions.assertEquals(Fraction.of(Integer.MIN_VALUE, -1), e);
+
+        Assertions.assertThrows(ArithmeticException.class, () -> Fraction.from(1e10));
+    }
+
+    @Test
+    void testFraction2() {
+        Fraction f1 = Fraction.of(-240, 256);
+        Fraction f2 = Fraction.of(15, -16);
+
+        Assertions.assertEquals(-1, f1.signum());
+        Assertions.assertTrue(f1.equals(f2));
+        Assertions.assertEquals(0, f1.compareTo(f2));
+        Assertions.assertEquals(-1, f1.compareTo(Fraction.ZERO));
+
+        Assertions.assertEquals(0, Fraction.of(Integer.MIN_VALUE, Integer.MIN_VALUE).compareTo(Fraction.ONE));
+    }
+
+    @Test
+    void testFraction3() {
+        Fraction result = Fraction.of(1, 2).add(Fraction.of(1, 3))
+                                           .add(Fraction.of(1, 6));
+        Assertions.assertEquals(Fraction.ONE, result);
+        Assertions.assertEquals(1.0, result.doubleValue(), "exact");
+        Assertions.assertEquals(0.9999999999999999, 1.0 / 2 + 1.0 / 3 + 1.0 / 6, "inexact");
+    }
+
+    @Test
+    void testFraction4() {
+        double a = Fraction.of(1, 8).doubleValue();
+        double b = Fraction.of(1, 3).doubleValue();
+        int c = Fraction.of(8, 3).intValue();
+        Assertions.assertEquals(0.125, a, "exact");
+        Assertions.assertEquals(1.0 / 3, b, "inexact");
+        Assertions.assertEquals(2, c, "inexact, whole number part of 2 2/3");
+    }
+
+    @Test
+    void testBigFraction1() {
+        BigFraction a = BigFraction.from(1.0 / 3);
+        Assertions.assertEquals(BigFraction.of(6004799503160661L, 18014398509481984L), a);
+
+        BigFraction b = BigFraction.from(1.0 / 3, 3);
+        Assertions.assertEquals(BigFraction.of(1, 3), b);
+    }
+
+    /**
+     * Test tan(z). See:
+     * https://en.wikipedia.org/wiki/Trigonometric_functions#Continued_fraction_expansion
+     */
+    @Test
+    void testContinuedFraction1() {
+        final double eps = 1e-8;
+        final ContinuedFraction cf = new ContinuedFraction() {
+            @Override
+            public double getA(int n, double x) {
+                return n < 2 ? x : -x * x;
+            }
+
+            @Override
+            public double getB(int n, double x) {
+                return n == 0 ? 0 : 2 * n - 1;
+            }
+        };
+        double z = 0.125;
+        double result = cf.evaluate(z, eps);
+        double expected = Math.tan(z);
+        Assertions.assertEquals(expected, result, expected * eps * 10);
+    }
+
+    @Test
+    void testGeneralizedContinuedFraction1() {
+        double eps = 1e-10;
+        // Golden ratio
+        Supplier<Coefficient> gen = () -> Coefficient.of(1, 1);
+        double gr1 = GeneralizedContinuedFraction.value(gen, eps);
+        double gr2 = GeneralizedContinuedFraction.value(1, gen, eps);
+        final double expected = 1.6180339887498948;
+        Assertions.assertEquals(expected, gr1, expected * eps * 10);
+        Assertions.assertEquals(expected, gr2, expected * eps * 10);
+    }
+
+    @Test
+    void testGeneralizedContinuedFraction2() {
+        double z = 0.125;
+        double tan1 = GeneralizedContinuedFraction.value(0, new Tan(z));
+
+        // Advance 1 term
+        Tan t = new Tan(z);
+        Coefficient c = t.get();
+        double tan2 = c.getA() / GeneralizedContinuedFraction.value(c.getB(), t);
+
+        // Avoid JDK variations in Math.tan(z)
+        // https://keisan.casio.com/calculator
+        double expected = 0.1256551365751309677927;
+        Assertions.assertEquals(expected, tan1, 2 * Math.ulp(expected));
+        Assertions.assertEquals(expected, tan2);
+    }
+
+    @Test
+    void testGeneralizedContinuedFraction3() {
+        double result1 = GeneralizedContinuedFraction.value(GeneralizedContinuedFractionTest.simpleContinuedFraction(4, 2, 6, 7));
+        double result2 = GeneralizedContinuedFraction.value(4, GeneralizedContinuedFractionTest.simpleContinuedFraction(2, 6, 7));
+        double expected = 415.0 / 93.0;
+        Assertions.assertEquals(expected, result1, Math.ulp(expected));
+        Assertions.assertEquals(expected, result2, Math.ulp(expected));
+    }
+}
diff --git a/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/UserGuideTest.java b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/UserGuideTest.java
new file mode 100644
index 00000000..146155a1
--- /dev/null
+++ b/commons-numbers-gamma/src/test/java/org/apache/commons/numbers/gamma/UserGuideTest.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.gamma;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testGamma1() {
+        double result = Erfc.value(1.23);
+        Assertions.assertTrue(0 <= result && result <= 2);
+
+        // Default function evaluation
+        double a = 1.23;
+        double x = 4.56;
+        double result1 = IncompleteGamma.Lower.value(a, x);
+
+        // Parameterize function evaluation
+        double epsilon = 1e-10;
+        int maxIterations = 1000;
+        double result2 = IncompleteGamma.Lower.value(a, x, epsilon, maxIterations);
+        Assertions.assertEquals(result1, result2, result1 * 1e-14);
+    }
+}
diff --git a/commons-numbers-primes/src/test/java/org/apache/commons/numbers/primes/UserGuideTest.java b/commons-numbers-primes/src/test/java/org/apache/commons/numbers/primes/UserGuideTest.java
new file mode 100644
index 00000000..43c25420
--- /dev/null
+++ b/commons-numbers-primes/src/test/java/org/apache/commons/numbers/primes/UserGuideTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.primes;
+
+import java.util.Arrays;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testPrimes1() {
+        int n = 51237173;
+        boolean prime = Primes.isPrime(n);
+        int     m     = Primes.nextPrime(n);
+        Assertions.assertFalse(prime);
+        Assertions.assertEquals(51237233, m);
+
+        List<Integer> f1 = Primes.primeFactors(n);
+        List<Integer> f2 = Primes.primeFactors(m);
+        Assertions.assertEquals(Arrays.asList(13, 863, 4567), f1);
+        Assertions.assertEquals(Arrays.asList(m), f2);
+        // n == 13 * 863 * 4567
+        Assertions.assertEquals(n, f1.stream().mapToInt(i -> i).reduce((i, j) -> i * j).getAsInt());
+    }
+}
diff --git a/commons-numbers-quaternion/src/test/java/org/apache/commons/numbers/quaternion/SlerpTest.java b/commons-numbers-quaternion/src/test/java/org/apache/commons/numbers/quaternion/SlerpTest.java
index 37f34c76..56f2fcb4 100644
--- a/commons-numbers-quaternion/src/test/java/org/apache/commons/numbers/quaternion/SlerpTest.java
+++ b/commons-numbers-quaternion/src/test/java/org/apache/commons/numbers/quaternion/SlerpTest.java
@@ -309,10 +309,10 @@ class SlerpTest {
 
     /**
      * Create a quaterion representing a rotation around the +z axis.
-     * @param theta
-     * @return
+     * @param theta Rotation angle
+     * @return the quaternion
      */
-    private static Quaternion createZRotation(final double theta) {
+    static Quaternion createZRotation(final double theta) {
         double halfAngle = theta * 0.5;
 
         return Quaternion.of(Math.cos(halfAngle), 0, 0, Math.sin(halfAngle));
diff --git a/commons-numbers-quaternion/src/test/java/org/apache/commons/numbers/quaternion/UserGuideTest.java b/commons-numbers-quaternion/src/test/java/org/apache/commons/numbers/quaternion/UserGuideTest.java
new file mode 100644
index 00000000..60757fed
--- /dev/null
+++ b/commons-numbers-quaternion/src/test/java/org/apache/commons/numbers/quaternion/UserGuideTest.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.quaternion;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testQuaternion1() {
+        double w = 2;
+        double x = 3;
+        double y = 4;
+        double z = 5;
+        Quaternion h1 = Quaternion.of(w, x, y, z);
+        Quaternion h2 = Quaternion.of(w, new double[] {x, y, z});
+        Assertions.assertEquals(w, h1.getW());
+        Assertions.assertEquals(w, h1.getScalarPart());
+        Assertions.assertArrayEquals(new double[] {x, y, z}, h1.getVectorPart());
+        Assertions.assertEquals(h1, h2);
+    }
+
+    @Test
+    void testQuaternion2() {
+        Quaternion h = Quaternion.of(1, 2, 3, 4);
+        Quaternion h1 = h.add(Quaternion.ONE);
+        Quaternion hi = h.add(Quaternion.I);
+        Quaternion hj = h.add(Quaternion.J);
+        Quaternion hk = h.add(Quaternion.K);
+        Assertions.assertEquals(Quaternion.of(2, 2, 3, 4), h1);
+        Assertions.assertEquals(Quaternion.of(1, 3, 3, 4), hi);
+        Assertions.assertEquals(Quaternion.of(1, 2, 4, 4), hj);
+        Assertions.assertEquals(Quaternion.of(1, 2, 3, 5), hk);
+    }
+
+    @Test
+    void testQuaternion3() {
+        Quaternion h1 = Quaternion.of(1, 2, 3, 4);
+        Quaternion h2 = Quaternion.of(5, 6, 7, 8);
+        Quaternion result1 = h1.multiply(h2);
+        Quaternion result2 = Quaternion.multiply(h1, h2);
+        Assertions.assertEquals(result1, result2);
+    }
+
+    @Test
+    void testQuaternion4() {
+        Quaternion q1 = Quaternion.of(1, 2, 3, 4);
+        Quaternion q2 = Quaternion.of(1, 2, 3, 4 + 1e-10);
+        Assertions.assertFalse(q1.equals(q2));
+        Assertions.assertTrue(q1.equals(q2, 1e-9));
+    }
+
+    @Test
+    void testSlerp1() {
+        Quaternion q1 = SlerpTest.createZRotation(0.75 * Math.PI);
+        Quaternion q2 = SlerpTest.createZRotation(0.76 * Math.PI);
+
+        Slerp slerp = new Slerp(q1, q2);
+
+        Assertions.assertTrue(slerp.apply(0.0).equals(q1, 1e-7));
+        Assertions.assertTrue(slerp.apply(0.25).equals(SlerpTest.createZRotation(0.7525 * Math.PI), 1e-7));
+        Assertions.assertTrue(slerp.apply(0.5).equals(SlerpTest.createZRotation(0.755 * Math.PI), 1e-7));
+        Assertions.assertTrue(slerp.apply(0.75).equals(SlerpTest.createZRotation(0.7575 * Math.PI), 1e-7));
+        Assertions.assertTrue(slerp.apply(0.0).equals(q1, 1e-7));
+    }
+}
diff --git a/commons-numbers-rootfinder/src/test/java/org/apache/commons/numbers/rootfinder/UserGuideTest.java b/commons-numbers-rootfinder/src/test/java/org/apache/commons/numbers/rootfinder/UserGuideTest.java
new file mode 100644
index 00000000..8ae98b99
--- /dev/null
+++ b/commons-numbers-rootfinder/src/test/java/org/apache/commons/numbers/rootfinder/UserGuideTest.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.numbers.rootfinder;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for examples contained in the user guide.
+ */
+class UserGuideTest {
+
+    @Test
+    void testBrentSolver1() {
+        double relAccuracy = 1e-6;
+        double absAccuracy = 1e-14;
+        double functionAccuracy = 1e-15;
+        BrentSolver solver = new BrentSolver(relAccuracy,
+                                             absAccuracy,
+                                             functionAccuracy);
+        double result1 = solver.findRoot(Math::sin, 3, 4);
+        double result2 = solver.findRoot(Math::sin, 3, 3.14, 4); // With initial guess
+        // result1 ~ result2 ~ Math.PI
+        Assertions.assertEquals(Math.PI, result1, Math.PI * relAccuracy);
+        Assertions.assertEquals(Math.PI, result2, Math.PI * relAccuracy);
+
+        // *** Throws an IllegalArgumentException ***
+        Assertions.assertThrows(IllegalArgumentException.class,
+            () -> solver.findRoot(Math::sin, 2, 3));
+    }
+}
diff --git a/src/site/apt/userguide/index.apt b/src/site/apt/userguide/index.apt
new file mode 100644
index 00000000..ac76376e
--- /dev/null
+++ b/src/site/apt/userguide/index.apt
@@ -0,0 +1,991 @@
+~~
+~~ Licensed to the Apache Software Foundation (ASF) under one or more
+~~ contributor license agreements.  See the NOTICE file distributed with
+~~ this work for additional information regarding copyright ownership.
+~~ The ASF licenses this file to You under the Apache License, Version 2.0
+~~ (the "License"); you may not use this file except in compliance with
+~~ the License.  You may obtain a copy of the License at
+~~
+~~      http://www.apache.org/licenses/LICENSE-2.0
+~~
+~~ Unless required by applicable law or agreed to in writing, software
+~~ distributed under the License is distributed on an "AS IS" BASIS,
+~~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~~ See the License for the specific language governing permissions and
+~~ limitations under the License.
+~~
+
+The Apache Commons Numbers User Guide
+
+* Table of contents
+
+  * {{{Overview}Overview}}
+
+    * {{{Examples_Modules}Examples Modules}}
+
+  * {{{Angle}Angle}}
+
+  * {{{Arrays}Arrays}}
+
+  * {{{Combinatorics}Combinatorics}}
+
+  * {{{Complex}Complex}}
+
+  * {{{Core}Core}}
+
+    * {{{Precision}Precision}}
+
+  * {{{Field}Field}}
+
+  * {{{Fraction}Fraction}}
+
+    * {{{Rational_Numbers}Rational Numbers}}
+
+    * {{{Continued_Fractions}Continued Fractions}}
+
+  * {{{Gamma}Gamma}}
+
+  * {{{Primes}Primes}}
+
+  * {{{Quaternion}Quaternion}}
+
+  * {{{Root_Finder}Root Finder}}
+
+  * {{{Dependencies}Dependencies}}
+
+Overview
+
+  <<<Apache Commons Numbers>>> provides number types and utilities.
+
+  The code originated in the {{{https://commons.apache.org/proper/commons-math/}commons-math}}
+  project but was pulled out into a separate project for better
+  maintainability and has since undergone numerous improvements.
+
+  The library is divided into modules:
+
+  * {{{../commons-numbers-angle/index.html}commons-numbers-angle}} - Utilities related to the concept of angle.
+
+  * {{{../commons-numbers-arrays/index.html}commons-numbers-arrays}} - Array utilities.
+
+  * {{{../commons-numbers-combinatorics/index.html}commons-numbers-combinatorics}} - Combinatorics utilities such as factorial and binomial coefficients.
+
+  * {{{../commons-numbers-complex/index.html}commons-numbers-complex}} - Utilities for working with complex numbers.
+
+  * {{{../commons-numbers-core/index.html}commons-numbers-core}} - Basic utilities.
+
+  * {{{../commons-numbers-field/index.html}commons-numbers-field}} - Utilities related to the concept of field.
+
+  * {{{../commons-numbers-fraction/index.html}commons-numbers-fraction}} - Utilities related to fractions such as rational numbers and continued fractions.
+
+  * {{{../commons-numbers-gamma/index.html}commons-numbers-gamma}} - Utilities related to the "Gamma" function.
+
+  * {{{../commons-numbers-primes/index.html}commons-numbers-primes}} - Utilities related to prime numbers.
+
+  * {{{../commons-numbers-quaternion/index.html}commons-numbers-quaternion}} - Utilities for working with quarternion numbers.
+
+  * {{{../commons-numbers-rootfinder/index.html}commons-numbers-rootfinder}} - Utilities for finding the zero of a function.
+
+  []
+
+  The {{{../commons-numbers-bom/index.html}commons-numbers-bom}} artifact provides a
+  Bill of Materials (BOM) to aid in dependency management of the modules.
+
+* Examples Modules
+
+  In addition to the modules above, the Commons Numbers
+  {{{https://commons.apache.org/numbers/download_numbers.cgi}source distribution}}
+  contains example code demonstrating library functionality and/or providing useful
+  development utilities. These modules are not part of the public API of the library and no
+  guarantees are made concerning backwards compatibility. The
+  {{{../commons-numbers-examples/modules.html}example module parent page}}
+  contains a listing of available modules.
+
+Angle
+
+  The {{{../commons-numbers-angle/index.html}commons-numbers-angle}} module
+  provides utilities related to the concept of angle
+  ({{{../commons-numbers-angle/apidocs/org/apache/commons/numbers/angle/package-summary.html}angle API}}).
+
+  The <<<Angle>>> class can be used to convert angles between common units. Sub-classes are used
+  to represent the angle units of degrees, radians and turn.
+
++------------------------------------------+
+Angle.Deg a = Angle.Turn.of(0.5).toDeg();
+Angle.Rad b = a.toRad();
+Angle.Turn c = b.toTurn();
+// c.getAsDouble() == 0.5
++------------------------------------------+
+
+  A normalizer can be used to map any value to the natural interval of the angle unit around a
+  provided lower bound, for example <<<[lo, lo + 360)>>> for degrees:
+
++------------------------------------------+
+// Range: [-180, 180)
+double angle = Angle.Deg.normalizer(-180).applyAsDouble(270);
+// angle == -90.0
++------------------------------------------+
+
+  The normalizer makes use of the <<<Reduce>>> class which can be applied to any interval:
+
++------------------------------------------+
+Reduce reduce = new Reduce(0, 24);
+double hours1 = reduce.applyAsDouble(173.5);
+double hours2 = reduce.applyAsDouble(23.5);
+double hours3 = reduce.applyAsDouble(-10);
+// hours1 == 5.5
+// hours2 == 23.5
+// hours3 == 14.0
++------------------------------------------+
+
+  The <<<Angle>>> class is used extensively in the
+  {{{https://commons.apache.org/proper/commons-geometry/}commons-geometry}} project.
+
+  The <<<CosAngle>>> class computes the cosine of the angle between two vectors using the
+  dot product of two vectors and their magnitudes:
+
+  \( \cos\theta = \frac\{\mathbf\{a\}\cdot\mathbf\{b\}\}\{ \|\mathbf\{a\}\|\ \|\mathbf\{b\}\| \} \)
+
+  The computation uses extended precision methods to increase the overall accuracy of the result
+  at the cost of a moderate increase in the number of computations.
+
++------------------------------------------+
+double[] v1 = {1, 0};
+double[] v2 = {0, 1};
+double[] v3 = {7, 7};
+double cosAngle1 = CosAngle.value(v1, v1);
+double cosAngle2 = CosAngle.value(v1, v2);
+double cosAngle3 = CosAngle.value(v1, v3);
+// cosAngle1 == 1
+// cosAngle2 == 0
+// cosAngle3 == 0.7071067811865476  (sqrt(2) / 2)
+// Math.toDegrees(Math.acos(cosAngle3)) == 45
++------------------------------------------+
+
+Arrays
+
+  The {{{../commons-numbers-arrays/index.html}commons-numbers-arrays}} module
+  provides array utilities
+  ({{{../commons-numbers-arrays/apidocs/org/apache/commons/numbers/arrays/package-summary.html}arrays API}}).
+
+  The <<<SortInPlace>>> class can sort a number of arrays using the order imposed by a specified
+  array:
+
++------------------------------------------+
+double[] x = {3, 1, 2};
+double[] y = {1, 2, 3};
+double[] z = {0, 5, 7};
+SortInPlace.ASCENDING.apply(x, y, z);
+// x == [1, 2, 3]
+// y == [2, 3, 1]
+// z == [5, 7, 0]
++------------------------------------------+
+
+  The <<<MultidimensionalCounter>>> provides a mapping between a unidimensional storage and a
+  multidimensional conceptual structure. Note that successive values for the unidimensional
+  index are used for successive values of the <last> index in the indices of the multidimensional
+  structure. When the dimension size is reached the values roll-over to the next dimension.
+  The class ensures a 1-to-1 mapping from any index within the size to a multidimensional position.
+
++------------------------------------------+
+MultidimensionalCounter c = MultidimensionalCounter.of(100, 50);
+int size = c.getSize();
+// size == 5000
+
+int index = 233;
+int[] indices1 = c.toMulti(index);
+int[] indices2 = c.toMulti(index + 1);
+// indices1 = [4, 33]  :  4 * 50 + 33 == 233
+// indices2 = [4, 34]  :  4 * 50 + 34 == 234
+
+int index1 = c.toUni(4, 33);      // varargs
+int index2 = c.toUni(indices1);
+// index == index1 == index2
+
+c.toMulti(49)     // [ 0, 49]
+c.toMulti(50)     // [ 1,  0]
+c.toMulti(4999)   // [99, 49]
++------------------------------------------+
+
+Combinatorics
+
+  The {{{../commons-numbers-combinatorics/index.html}commons-numbers-combinatorics}} module
+  provides combinatorics utilities such as factorial and binomial coefficients
+  ({{{../commons-numbers-combinatorics/apidocs/org/apache/commons/numbers/combinatorics/package-summary.html}combinatorics API}}).
+
+  The binomial coefficient \( \binom\{n\}\{k\} \) can be evaluated as a <<<long>>>, <<<double>>>
+  or using the natural logarithm.
+
++------------------------------------------+
+long   bc1 = BinomialCoefficient.value(63, 33);
+double bc2 = BinomialCoefficientDouble.value(1029, 514);
+double bc3 = LogBinomialCoefficient.value(152635712, 125636);
+// bc1 == 7219428434016265740
+// bc2 ~ 1.429820686498904e308
+// bc3 ~ 1017897.199659759
++------------------------------------------+
+
+  The factorial \( n! \) can be evaluated as a <<<long>>>, <<<double>>> or using the natural
+  logarithm.
+
++------------------------------------------+
+long   f1 = Factorial.value(15);
+double f2 = Factorial.doubleValue(170);
+double f3 = LogFactorial.create().value(Integer.MAX_VALUE);
+// f1 == 1307674368000
+// f2 == 7.257415615307999e306
+// f3 == 4.3996705655378525e10
++------------------------------------------+
+
+  Note that if the binomial coefficient or factorial cannot be represented the methods will
+  throw an <<<ArithmeticException>>> in-place of a <<<long>>> value or return infinity
+  in-place of a <<<double>>> value. The logarithm methods will always compute a valid result
+  given the bounds imposed by the <<<int>>> arguments.
+
+  The <<<LogFactorial>>> class can be created with a cache size. All values up to this size
+  are computed upon creation. This functionality can be used when a range of values may be
+  repeatedly required.
+
++------------------------------------------+
+LogFactorial lf = LogFactorial.create().withCache(50);
++------------------------------------------+
+
+  Values outside the cache are computed using <<<LogGamma.value(n + 1.0)>>> from the
+  {{{Gamma}Gamma}} module; this can be used to avoid the object creation of a single-use
+  instance of LogFactorial.
+
+  The combinations defined by the binomial coefficient \( \binom\{n\}\{k\} \) can be
+  iterated using the <<<Combinations>>> class:
+
++------------------------------------------+
+Combinations.of(4, 2).iterator().forEachRemaining(c -> System.out.println(Arrays.toString(c)));
++------------------------------------------+
+
+  Outputs:
+
++------------------------------------------+
+[0, 1]
+[0, 2]
+[1, 2]
+[0, 3]
+[1, 3]
+[2, 3]
++------------------------------------------+
+
+  The lexigraphical order is based on the values in the input array in reverse order. This ordering
+  can be imposed on arbitrary sets using the <<<Comparator\<int[]\>>>> provided by an appropriate
+  <<<Combination>>>:
+
++------------------------------------------+
+List<int[]> list = Arrays.asList(new int[][] {
+    {3, 4, 5},
+    {3, 1, 5},
+    {3, 2, 5},
+    {4, 2, 4},
+});
+list.sort(Combinations.of(6, 3).comparator());
+list.forEach(c -> System.out.println(Arrays.toString(c)));
++------------------------------------------+
+
+  Outputs:
+
++------------------------------------------+
+[4, 2, 4]
+[3, 1, 5]
+[3, 2, 5]
+[3, 4, 5]
++------------------------------------------+
+
+Complex
+
+  The {{{../commons-numbers-complex/index.html}commons-numbers-complex}} module
+  provides utilities for working with complex numbers
+  ({{{../commons-numbers-complex/apidocs/org/apache/commons/numbers/complex/package-summary.html}complex API}}).
+
+  The <<<Complex>>> class is a Cartesian representation of a complex number. The complex number is
+  expressed in the form \( a + ib \) where \( a \) and \( b \) are real numbers and \( i \)
+  is the imaginary unit which satisfies the equation \( i^2 = -1 \). For the
+  complex number \( a + ib \), \( a \) is called the <real part> and
+  \( b \) is called the <imaginary part>. Arithmetic in this class conforms to the C99
+  standard for complex numbers defined in
+  {{{https://www.open-std.org/JTC1/SC22/WG14/www/standards}ISO/IEC 9899}}, Annex G.
+
+  The <<<Complex>>> class is immutable. Instances are constructed with factory methods:
+
++------------------------------------------+
+double x = 3;
+double y = 4;
+Complex c1 = Complex.ofCartesian(x, y);
+// c1 == x + iy
+
+// Convert from polar coordinates
+double rho = 1.23;
+double theta = Math.PI / 2;
+Complex c2 = Complex.ofPolar(rho, theta);
++------------------------------------------+
+
+  Arithmetic operations, elementary functions and trigonometric functions are provided that return
+  new instances of <<<Complex>>> or primitive data types:
+
++------------------------------------------+
+Complex c1 = Complex.ofCartesian(3, 4);
+Complex c2 = Complex.ofCartesian(5, 6);
+Complex c3 = c1.multiply(c2).sqrt();
+
+double magnitude = c3.abs();
+double argument = c3.arg();
+
+boolean finite = c3.isFinite();
++------------------------------------------+
+
+Core
+
+  The {{{../commons-numbers-core/index.html}commons-numbers-core}} module
+  provides basic utilities
+  ({{{../commons-numbers-core/apidocs/org/apache/commons/numbers/core/package-summary.html}core API}}).
+  The classes within this module provide core functionality
+  reused within <<<Apache Commons Numbers>>> and also other projects such as
+  {{{https://commons.apache.org/proper/commons-geometry/}commons-geometry}} and
+  {{{https://commons.apache.org/proper/commons-math/}commons-math}}.
+
+  The interfaces <<<Addition>>> and <<<Multiplication>>> provide generic typed definitions of
+  the arithmetic <add> and <multiply> operations. These are extended by <<<NativeOperators>>>
+  that adds operators that can be implemented in a more performant way. These interfaces are used
+  by the {{{Field}Field}} module.
+
+  The <<<ArithmeticUtils>>> class provides arithmetic utilities such as integer power, unsigned
+  divide and remainder functions; greatest common divisor; and least common multiple. These
+  functions are used by the {{{Fraction}Fraction}} module.
+
+  The <<<Norm>>> class provides implementations of
+  {{{https://en.wikipedia.org/wiki/Norm_(mathematics)}norm}} functions. The implementations
+  may use extended precision methods to increase the overall accuracy of the result
+  at the cost of a moderate increase in the number of computations. The Euclidean implementation
+  handles possible under and overflow of intermediates using scaling.
+
++------------------------------------------+
+double x = Norm.EUCLIDEAN.of(3, -4);                              // 5
+double y = Norm.MANHATTAN.of(3, -4, 5);                           // 12
+double z = Norm.MAXIMUM.of(new double[]{3, -4, 5, -6, -7, -8});   // 8
+
+double big = Double.MAX_VALUE * 0.5;
+double length = Norm.EUCLIDEAN.of(big, big, big);
+// length == Math.sqrt(0.5 * 0.5 * 3) * Double.MAX_VALUE
++------------------------------------------+
+
+  The <<<Sum>>> class provides accurate floating-point sums and linear combinations.
+  The class uses techniques to mitigate round off errors resulting from
+  standard floating-point operations, increasing the overall accuracy of
+  results at the cost of a moderate increase in the number of computations.
+  The <<<Sum>>> can add numbers and products of numbers.
+  The result is returned as the high-precision value if it is finite, or the standard IEEE754
+  result otherwise.
+
++------------------------------------------+
+double sum1 = Sum.create().add(1)
+                          .addProduct(3, 4)
+                          .getAsDouble();
+double sum2 = Sum.of(1).addProduct(3, 4)
+                       .getAsDouble();
+double sum3 = Sum.ofProducts(new double[] {3, 4}, new double[] {5, 6})
+                 .getAsDouble();
+// sum1 == sum2 == 13.0
+// sum3 == 3 * 5 + 4 * 6
+
+Sum.of(1, 2, Double.NaN).getAsDouble()                 // NaN
+Sum.of(1, 2, Double.NEGATIVE_INFINITY).getAsDouble()   // -inf
++------------------------------------------+
+
+  The implementation provides an effective 106 bit floating point significand. However the
+  significand is split into two <<<double>>> values which may be separated by more than <<<2^53>>>
+  by using the exponent of the second <<<double>>>.
+  This provides protection against cancellation in situations that would not be handled by an
+  IEEE754 binary 128 format with a 113 bit significand:
+
++------------------------------------------+
+double x1 = 1e100 + 1 - 2 - 1e100;
+double x2 = Sum.of(1e100, 1, -2, -1e100).getAsDouble();
+// x1 == 0.0
+// x2 == -1.0
++------------------------------------------+
+
+  The class can be used to:
+
+  * Reduce cancellation effects that occur when large magnitude numbers
+    with opposite signs are summed together with relatively small values;
+
+  * Provide accurate summation of numbers with the same sign irrespective of input order.
+
+  []
+
+* Precision
+
+  The <<<Precision>>> class provides comparison of floating-point numbers using relative, absolute
+  and unit of least precision ({{{https://en.wikipedia.org/wiki/Unit_in_the_last_place}ULP}})
+  tolerances. Comparison using the relative tolerance \( \epsilon \) is symmetric as the relative
+  delta is computed using the largest magnitude of the input pair of numbers:
+
+  \( \frac\{|a - b|\}\{\operatorname\{max\}(|a|, |b|)\} \le \epsilon \)
+
+  Comparisons using the ULP argument specifies the allowed distance <between> two numbers.
+  Numbers are equal if they have <<<(ulp - 1)>>> representable numbers or fewer <between> <<<a>>>
+  and <<<b>>>. Use <<<ulp = 0>>> for binary equality.
+
+  Note: The default implementation for equality allows no numbers <between> <<<a>>> and <<<b>>>.
+  It is equivalent to <<<Precision.equals(a, b, 1)>>>. Thus using a floating-point delta of
+  <<<0.0>>> may indicate numbers are equal when they are not <binary> equal (see example below).
+
++------------------------------------------+
+// Default allows no numbers between
+Precision.equals(1000.0, 1000.0)                              // true
+Precision.equals(1000.0, 1000.0 + Math.ulp(1000.0))           // true
+Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0))       // false
+
+// Absolute - tolerance is floating-point
+Precision.equals(1000.0, 1001.0)                              // false
+Precision.equals(1000.0, 1001.0, 1.0)                         // true
+Precision.equals(1000.0, 1000.0 + Math.ulp(1000.0), 0.0)      // true  (but not binary equal)
+
+// ULP - tolerance is integer
+Precision.equals(1000.0, 1001.0)                              // false
+Precision.equals(1000.0, 1001.0, 1)                           // false
+Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0), 1)    // false
+Precision.equals(1000.0, 1000.0 + 2 * Math.ulp(1000.0), 2)    // true  (n - 1) numbers between
+Precision.equals(1000.0, 1000.0 + 3 * Math.ulp(1000.0), 2)    // false
+
+// Relative
+Precision.equalsWithRelativeTolerance(1000.0, 1001.0, 1e-6)   // false
+Precision.equalsWithRelativeTolerance(1000.0, 1001.0, 1e-3)   // true
++------------------------------------------+
+
+  Equality using <<<NaN>>> values will be <<<false>>>. To allow two <<<NaN>>> values to be
+  equal requires the use of the method <<<equalsIncludingNaN>>>. Special handling of <<<NaN>>>
+  values allows the default implementation to be optimized for the typical use case comparing
+  finite numbers. Note that infinity values are equal.
+
++------------------------------------------+
+Precision.equals(Double.NaN, 1000.0)                                   // false
+Precision.equals(Double.NaN, Double.NaN)                               // false
+Precision.equalsIncludingNaN(Double.NaN, Double.NaN)                   // true
+
+Precision.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY)   // true
+Precision.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY)   // true
+Precision.equals(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY)   // false
++------------------------------------------+
+
+  <<<Precision>>> provides a <<<compareTo>>> method to provide ordering of numbers that are
+  not considered equal using the tolerance:
+
++------------------------------------------+
+Precision.compareTo(100, 100, 0.0)   // 0
+Precision.compareTo(100, 101, 1.0)   // 0
+Precision.compareTo(100, 102, 1.0)   // -1
+Precision.compareTo(102, 100, 1.0)   // 1
++------------------------------------------+
+
+  The comparison uses the natural ordering and is symmetric with regard to <<<NaN>>> values.
+  The comparison using <<<\<, ==, \>>>> is extended in the <<<DoubleEquivalence>>> interface
+  that supports <<<\<=, \>=>>> and signum.
+
++------------------------------------------+
+Precision.DoubleEquivalence eq = Precision.doubleEquivalenceOfEpsilon(1.0);
+eq.lt(100, 100)    // false
+eq.lte(100, 100)   // true
+eq.eq(100, 100)    // true
+eq.gte(100, 100)   // true
+eq.gt(100, 100)    // false
++------------------------------------------+
+
+  <<<Precision>>> also provides methods for other floating-point operations such as rounding and
+  computing a representable delta from a number and an ideal delta.
+
++------------------------------------------+
+// Round to a number of digits to the right of the decimal point
+Precision.round(678.125, 4)              // 678.125
+Precision.round(678.125, 3)              // 678.125
+Precision.round(678.125, 2)              // 678.13
+Precision.round(678.125, 1)              // 678.1
+Precision.round(678.125, 0)              // 678.0
+Precision.round(678.125, -1)             // 680.0
+Precision.round(678.125, -2)             // 700.0
+
+Precision.representableDelta(1.0, 0.1)   // 0.10000000000000009
++------------------------------------------+
+
+Field
+
+  The {{{../commons-numbers-field/index.html}commons-numbers-field}} module
+  provides utilities related to the concept of field
+  ({{{../commons-numbers-field/apidocs/org/apache/commons/numbers/field/package-summary.html}field API}}).
+
+  A field is any set of elements that satisfies the field axioms for both addition and
+  multiplication and is a commutative division algebra. The <<<Field>>> interface
+  defines the operations of a field. Implementations are provided for rational numbers
+  as <<<FractionField>>> and <<<BigFractionField>>>; and real numbers as <<<FP64Field>>>.
+
++------------------------------------------+
+Field<FP64> field = FP64Field.get();
+FP64 result = field.one().multiply(3).divide(field.one().multiply(4));
+double value = result.doubleValue();
+// value = 0.75
++------------------------------------------+
+
+  The field abstraction can be used to program computations independently of the underlying
+  representation as any <<<Field\<T\>>>> can be manipulated using the arithmetic operations of
+  add, subtract, multiply and divide.
+
+Fraction
+
+  The {{{../commons-numbers-fraction/index.html}commons-numbers-fraction}} module
+  provides fraction utilities
+  ({{{../commons-numbers-fraction/apidocs/org/apache/commons/numbers/fraction/package-summary.html}fraction API}}).
+  Classes are provided to represent rational numbers and to evaluate continued fractions.
+
+* Rational Numbers
+
+  The <<<Fraction>>> class is a representation of a rational number <<<p / q>>> using
+  <<<int>>> values for the numerator <<<p>>> and denominator <<<q>>>.
+
+  Instances are constructed with factory methods:
+
++------------------------------------------+
+int p = 3;
+int q = 4;
+Fraction f = Fraction.of(p, q);
+// f == 3 / 4
++------------------------------------------+
+
+  <<<Fraction>>> can be created using a <<<double>>>. The floating-point number may not be exactly
+  representable; a continued fraction is used to converge on a rational representation within a
+  specified absolute tolerance, or a maximum denominator. An <<<ArithmeticException>>> is thrown
+  if the algorithm fails to converge or the number is not representable.
+
++------------------------------------------+
+int maxIterations = 100;
+//                               tolerance
+Fraction a = Fraction.from(0.6152, 0.02,     maxIterations);
+Fraction b = Fraction.from(0.6152, 1.0e-7,   maxIterations);
+// a = 3 / 5      == 0.6
+// b = 769 / 1250 == 0.6152 (exact)
+
+//                               max denominator
+Fraction c = Fraction.from(0.6152, 9);
+Fraction d = Fraction.from(0.6152, 9999);
+// c = 3 / 5      == 0.6
+// d = 769 / 1250 == 0.6152 (exact)
+
+Fraction e = Fraction.from(Math.pow(2, 31));
+// e = Integer.MIN_VALUE / -1
+
+// *** Throws an ArithmeticException ***
+Fraction.from(Math.pow(2, 32));
++------------------------------------------+
+
+  The fraction is represented in reduced form using the greatest common divisor. For example
+  <<<240 / 256>>> is reduced to <<<15 / 16>>>. The sign can be either in the numerator or
+  the denominator; the sign of the fraction is accessed using the <<<signum()>>> method.
+  Fraction equality can be evaluated using <<<equals()>>>. The class implements <<<Comparable\<Fraction\>>>>
+  to allow sorting using the natural order imposed by the fraction's sign and magnitude.
+
++------------------------------------------+
+Fraction f1 = Fraction.of(-240, 256);
+Fraction f2 = Fraction.of(15, -16);
+
+f1.signum()                 == -1
+f1.equals(f2)               == true
+f1.compareTo(f2)            == 0
+f1.compareTo(Fraction.ZERO) == -1
+
+Fraction.of(Integer.MIN_VALUE, Integer.MIN_VALUE).compareTo(Fraction.ONE) == 0
++------------------------------------------+
+
+  Arithmetic operations are provided that return new instances of <<<Fraction>>>. Note that
+  fraction operations are <exact>.
+
++------------------------------------------+
+Fraction result = Fraction.of(1, 2).add(Fraction.of(1, 3))
+                                   .add(Fraction.of(1, 6));
+// result == 1 / 1
+// Note: 1.0 / 2 + 1.0 / 3 + 1.0 / 6 == 0.9999999999999999 (inexact)
++------------------------------------------+
+
+  <<<Fraction>>> extends <<<java.lang.Number>>> and can be converted to other primitive numbers
+  with possible loss of precision:
+
++------------------------------------------+
+double a = Fraction.of(1, 8).doubleValue();
+double b = Fraction.of(1, 3).doubleValue();
+int    c = Fraction.of(8, 3).intValue();
+// a == 0.125     (exact)
+// b == 1.0 / 3   (inexact)
+// c == 2         (inexact, whole number part of 2 2/3)
++------------------------------------------+
+
+  <<<Fraction>>> is limited to rational numbers representable using <<<int>>> values. Operations
+  that cannot be represented will raise an <<<ArithmeticException>>>. Intermediate
+  computations may use the <<<long>>> datatype and arithmetic is relatively fast.
+
+  The <<<BigFraction>>> class uses <<<BigInteger>>> for the numerator and denominator and can
+  provide exact arithmetic up to the limitations of <<<BigInteger>>>. This is documented as
+  at least <<<+/- 2^Integer.MAX_VALUE>>> (exclusive) and may support values outside of that range.
+
+  The <<<BigFraction>>> class provides all the arithmetic operations of <<<Fraction>>> and
+  can be used when <<<Fraction>>> cannot represent the rational numbers; there will be a performance
+  cost to using <<<BigFraction>>> due to the switch from primitive values to <<<BigInteger>>> for
+  computations.
+
+  <<<BigFraction>>> can be constructed from <<<int>>>, <<<long>>> or <<<BigInteger>>> numerator
+  and denominators.
+
++------------------------------------------+
+BigFraction a = BigFraction.of(1, 3);
+// a == 1 / 3
++------------------------------------------+
+
+  The <<<BigFraction>>> conversion of <<<double>>> values can be exact, or adjusted to be
+  within a provided tolerance. Note that exact conversion may create an unexpected rational
+  number as it is limited by the precision of the input <<<double>>> value which may be inexact:
+
++------------------------------------------+
+BigFraction a = BigFraction.from(1.0 / 3);
+// a == 6004799503160661 / 18014398509481984
+
+//                                        max denominator
+BigFraction b = BigFraction.from(1.0 / 3, 3);
+// b == 1 / 3
++------------------------------------------+
+
+* Continued Fractions
+
+  A generalized continued fraction is an expression of the form:
+
+  \( x = b_0 + \cfrac\{a_1\}\{b_1 + \cfrac\{a_2\}\{b_2 + \cfrac\{a_3\}\{b_3 + \cfrac\{a_4\}\{b_4 + \ddots\,\}\}\}\} \)
+
+  Classes are provided to evaluate the expression through iteration to a provided error tolerance.
+  The user must supply the coefficients \( a \) and \( b \).
+
+  The <<<ContinuedFraction>>> class requires a function to supply the <n>th coefficients; the
+  API allows the fraction function to be evaluated for different arguments. This is useful when
+  the coefficients can be computed using a formula based on the index and the argument.
+  The class is abstract and must be extended to provide the coefficients \( a \) and \( b \).
+  For example the following fraction:
+
+  \( \tan(z) = \cfrac\{z\}\{1 - \cfrac\{z^2\}\{3 - \cfrac\{z^2\}\{5 - \ddots\,\}\}\} \)
+
+  Can be computed using:
+
++------------------------------------------+
+ContinuedFraction cf = new ContinuedFraction() {
+    @Override
+    public double getA(int n, double x) {
+        return n < 2 ? x : -x * x;
+    }
+
+    @Override
+    public double getB(int n, double x) {
+        return n == 0 ? 0 : 2 * n - 1;
+    }
+};
+
+double eps = 1e-8;
+double z = 0.125;
+double result = cf.evaluate(z, eps);
+// result ~ Math.tan(z)
++------------------------------------------+
+
+  The <<<GeneralizedContinuedFraction>>> class requires a generator for the next pair of
+  coefficients. This is useful when the coefficients can be computed using the previous values.
+  The class evaluates the fraction using static methods and a supplied generator of coefficients.
+  This can be done with the generator supplying the initial term \( b_0 \) where \( a_0 \) is
+  ignored, or with the generator starting at \( (a_1, b_1) \) and the term \( b_0 \) provided
+  separately.
+
+  For example the golden ratio:
+
+  \( x = 1 + \cfrac\{1\}\{1 + \cfrac\{1\}\{1 + \cfrac\{1\}\{1 + \ddots\,\}\}\} \)
+
+  Can be computed as:
+
++------------------------------------------+
+double eps = 1e-10;
+double gr1 = GeneralizedContinuedFraction.value(() -> Coefficient.of(1, 1), eps);
+double gr2 = GeneralizedContinuedFraction.value(1, () -> Coefficient.of(1, 1), eps);
+// gr1 ~ gr2 ~ 1.6180339887498948...
++------------------------------------------+
+
+  Whether to separate the term \( b_0 \) depends on the fraction and the algorithm convergence.
+  See the
+  {{{../commons-numbers-fraction/apidocs/org/apache/commons/numbers/fraction/GeneralizedContinuedFraction.html}GeneralizedContinuedFraction}}
+  documentation for details. Note that it is also possible to evaluate part of the fraction using
+  only later terms and then computing the final result by including the early terms.
+
+  An example using a recursive generator for the evaluation of \( \tan(z) \) is computed using:
+
++------------------------------------------+
+static class Tan implements Supplier<Coefficient> {
+    private double a;
+    private double b = -1;
+
+    /**
+     * @param z Argument z
+     */
+    Tan(double z) {
+        a = z;
+    }
+
+    @Override
+    public Coefficient get() {
+        b += 2;
+        // Special first case
+        if (b == 1) {
+            double z = a;
+            // Update the term for the rest of the fraction.
+            // The continuant is subtracted from the b terms, thus all the
+            // remaining a terms are negative.
+            a *= -z;
+            return Coefficient.of(z, b);
+        }
+        return Coefficient.of(a, b);
+    }
+}
+
+double z = 0.125;
+double tan1 = GeneralizedContinuedFraction.value(0, new Tan(z));
+
+// Advance 1 term
+Tan t = new Tan(z);
+Coefficient c = t.get();
+double tan2 = c.getA() / GeneralizedContinuedFraction.value(c.getB(), t);
+
+// tan1 ~  Math.tan(z)
+// tan2 == Math.tan(z)  (more accurate)
++------------------------------------------+
+
+  A simple continued fraction will have an \( a \) coefficient of 1. For example the following
+  fraction:
+
+  \( \frac\{415\}\{93\} = 4 + \cfrac\{1\}\{2 + \cfrac\{1\}\{6 + \cfrac\{1\}\{7\}\}\} \)
+
+  can be evaluated as a simple continued fraction using:
+
++------------------------------------------+
+private static Supplier<Coefficient> simpleContinuedFraction(double... coefficients) {
+    return new Supplier<Coefficient>() {
+        /* iteration. */
+        private int n;
+        /* denominator terms. */
+        private double[] b = coefficients.clone();
+
+        @Override
+        public Coefficient get() {
+            if (n != b.length) {
+                // Return the next term
+                return Coefficient.of(1, b[n++]);
+            }
+            return Coefficient.of(0, 1);
+        }
+    };
+}
+
+double result1 = GeneralizedContinuedFraction.value(simpleContinuedFraction(4, 2, 6, 7));
+double result2 = GeneralizedContinuedFraction.value(4, simpleContinuedFraction(2, 6, 7));
+// result1 ~ result2 ~ 415.0 / 93.0
++------------------------------------------+
+
+Gamma
+
+  The {{{../commons-numbers-gamma/index.html}commons-numbers-gamma}} module
+  provides utilities related to the "Gamma" function
+  ({{{../commons-numbers-gamma/apidocs/org/apache/commons/numbers/gamma/package-summary.html}gamma API}}).
+
+*---+---+
+Gamma function | \\( \\Gamma(a) \\)
+*---+---+
+Incomplete gamma functions (lower and upper) | \\( \\Gamma(a) = \\gamma(a, x) + \\Gamma(a, x) \\)
+*---+---+
+Regularized gamma functions (lower and upper) | \\( 1 = P(a, x) + Q(a, x) \\)
+*---+---+
+Ratio of gamma functions | \\( \\frac\\{\\Gamma(a)\\}\\{\\Gamma(b)\\} \\)
+*---+---+
+Digamma function | \\( \\frac\\{d\\}\\{dx\\}(\\ln \\Gamma(x)) \\)
+*---+---+
+Trigamma function | \\( \\psi_1(x) = \\frac\\{d^2\\}\\{dx^2\\} (\\ln \\Gamma(x)) \\)
+*---+---+
+Logarithm of the absolute value of the gamma function | \\( \\ln\\, \\lvert \\Gamma(x) \\rvert \\)
+*---+---+
+Beta function | \\( B(a, b) = \\frac\\{\\Gamma(a)\\ \\Gamma(b)\\}\\{\\Gamma(a+b)\\} \\)
+*---+---+
+Incomplete beta function (and the complement) | \\( B_x(a, b) = \\int_0^x t^\\{a-1\\}\\,(1-t)^\\{b-1\\}\\,dt \\)
+*---+---+
+Logarithm of the beta function | \\( \\ln B(a, b) \\)
+*---+---+
+Error function | \\( \\operatorname\\{erf\\}(z) \\)
+*---+---+
+Complementary error function | \\( \\operatorname\\{erfc\\}(z) \\)
+*---+---+
+Scaled complementary error function | \\( \\operatorname\\{erfcx\\}(z) \\)
+*---+---+
+Difference between error function values | \\( \\operatorname\\{erf\\}(a) - \\operatorname\\{erf\\}(b) \\)
+*---+---+
+Inverse error functions |
+*---+---+
+
+  Functions are provided using static class methods. These may be optionally parameterized
+  if the method uses an iterative computation to control the function evaluation. For example:
+
++------------------------------------------+
+double result = Erfc.value(1.23);
+
+// Default function evaluation
+double a = 1.23;
+double x = 4.56;
+double result1 = IncompleteGamma.Lower.value(a, x);
+
+// Parameterize function evaluation
+double epsilon = 1e-10;
+int maxIterations = 1000;
+double result2 = IncompleteGamma.Lower.value(a, x, epsilon, maxIterations);
+// result1 ~ result2
++------------------------------------------+
+
+  The gamma functions are used to evaluate many of the probability distributions provided in the
+  {{{https://commons.apache.org/proper/commons-statistics/}commons-statistics}} project.
+
+Primes
+
+  The {{{../commons-numbers-primes/index.html}commons-numbers-primes}} module
+  provides utilities related to prime numbers
+  ({{{../commons-numbers-primes/apidocs/org/apache/commons/numbers/primes/package-summary.html}primes API}}).
+
+  The <<<Primes>>> class provides static methods for working with prime numbers in the range of
+  an <<<int>>>. Methods are provided to determine if a value is prime, compute the next prime
+  after a value and the prime factors of a number.
+
++------------------------------------------+
+int n = 51237173;
+boolean prime = Primes.isPrime(n);
+int     m     = Primes.nextPrime(n);
+// prime == false
+// m     == 51237233
+
+List<Integer> f1 = Primes.primeFactors(n);
+List<Integer> f2 = Primes.primeFactors(m);
+// f1 == [13, 863, 4567]
+// f2 == [51237233]
+// n  == 13 * 863 * 4567
++------------------------------------------+
+
+Quaternion
+
+  The {{{../commons-numbers-quaternion/index.html}commons-numbers-quaternion}} module
+  provides utilities for working with quarternion numbers
+  ({{{../commons-numbers-quaternion/apidocs/org/apache/commons/numbers/quaternion/package-summary.html}quaternion API}}).
+
+  The <<<Quaternion>>> class represents Hamilton's hypecomplex number in the form
+  \( H = w + x\,i + y\,j + z\,k \), where \( w \) is the scalar component and \( [x, y, z] \) is
+  the vector component.
+
+  The class is immutable and instances are constructed with factory methods. Getters are provided
+  for access to the components:
+
++------------------------------------------+
+double w = 2;
+double x = 3;
+double y = 4;
+double z = 5;
+Quaternion h1 = Quaternion.of(w, x, y, z);
+Quaternion h2 = Quaternion.of(w, new double[]{x, y, z});
+h1.getW();            // w
+h1.getScalarPart();   // w
+h1.getVectorPart();   // [x, y, z]
+// h1 == h2
++------------------------------------------+
+
+  Methods are provided for arithmetic (add, subtract, multiply) and other quaternion operations
+  such as scaling, dot product, negation, conjugation and normalization.
+
++------------------------------------------+
+Quaternion h = Quaternion.of(1, 2, 3, 4);
+Quaternion h1 = h.add(Quaternion.ONE);   // [2, 2, 3, 4]
+Quaternion hi = h.add(Quaternion.I);     // [1, 3, 3, 4]
+Quaternion hj = h.add(Quaternion.J);     // [1, 2, 4, 4]
+Quaternion hk = h.add(Quaternion.K);     // [1, 2, 3, 5]
++------------------------------------------+
+
+  The binary operations acting on two <<<Quaternion>>> instances are provided as instance
+  methods and static methods for convenience when using method references as lambda functions:
+
++------------------------------------------+
+Quaternion h1 = Quaternion.of(1, 2, 3, 4);
+Quaternion h2 = Quaternion.of(5, 6, 7, 8);
+Quaternion result1 = h1.multiply(h2);
+Quaternion result2 = Quaternion.multiply(h1, h2);
+// result1 == result2
++------------------------------------------+
+
+  The class evaluates binary equality using <<<equals(Object)>>> and provides a helper method
+  to evaluate equality within an absolute tolerance:
+
++------------------------------------------+
+Quaternion q1 = Quaternion.of(1, 2, 3, 4);
+Quaternion q2 = Quaternion.of(1, 2, 3, 4 + 1e-10);
+q1.equals(q2);         // false
+q1.equals(q2, 1e-9);   // true
++------------------------------------------+
+
+  Quaternions are used to apply rotations in 3-dimensional Euclidean space in the
+  {{{https://commons.apache.org/proper/commons-geometry/}commons-geometry}} project.
+
+  The <<<Slerp>>> class performs spherical linear interpolation
+  ({{{https://en.wikipedia.org/wiki/Slerp}Slerp}}). The algorithm is designed to interpolate
+  smoothly between two rotations/orientations, producing a constant-speed motion along an arc.
+
++------------------------------------------+
+static Quaternion createZRotation(final double theta) {
+    double halfAngle = theta * 0.5;
+    return Quaternion.of(Math.cos(halfAngle), 0, 0, Math.sin(halfAngle));
+}
+
+Quaternion q1 = createZRotation(0.75 * Math.PI);
+Quaternion q2 = createZRotation(0.76 * Math.PI);
+
+Slerp slerp = new Slerp(q1, q2);
+
+slerp.apply(0.0)    // ~ q1
+slerp.apply(0.25)   // ~ createZRotation(0.7525 * Math.PI)
+slerp.apply(0.5)    // ~ createZRotation(0.755  * Math.PI)
+slerp.apply(0.75)   // ~ createZRotation(0.7575 * Math.PI)
+slerp.apply(1.0)    // ~ q2
++------------------------------------------+
+
+Root Finder
+
+  The {{{../commons-numbers-rootfinder/index.html}commons-numbers-rootfinder}} module
+  provides utilities for finding the zero of a function
+  ({{{../commons-numbers-rootfinder/apidocs/org/apache/commons/numbers/rootfinder/package-summary.html}rootfinder API}}).
+
+  The <<<BrentSolver>>> class implements the Brent algorithm for finding the zeros of real
+  univariate functions. The solver requires an interval that brackets a root and will raise an
+  exception if there is no sign change between the bounds \( [a, b] \). The relative \( \epsilon \)
+  and absolute \( \delta \) accuracy specify the convergence tolerance for the function value
+  \( x \) to zero as: \( 2\,\epsilon\,|x| + \delta \).
+  The function accuracy is used to return the initial bracket bounds, or initial guess,
+  if the function value is within the specified accuracy of zero.
+
++------------------------------------------+
+double relAccuracy = 1e-6;
+double absAccuracy = 1e-14;
+double functionAccuracy = 1e-15;
+BrentSolver solver = new BrentSolver(relAccuracy,
+                                     absAccuracy,
+                                     functionAccuracy);
+double result1 = solver.findRoot(Math::sin, 3, 4);
+double result2 = solver.findRoot(Math::sin, 3, 3.14, 4);   // With initial guess
+// result1 ~ result2 ~ Math.PI
+
+// *** Throws an IllegalArgumentException ***
+solver.findRoot(Math::sin, 2, 3);
++------------------------------------------+
+
+Dependencies
+
+  Apache Commons Numbers requires JDK 1.8+ and has no runtime dependencies.
diff --git a/src/site/site.xml b/src/site/site.xml
index dcdf3d08..02e20845 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -23,6 +23,12 @@
   </bannerRight>
 
   <body>
+    <head>
+      <![CDATA[<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
+      </script>
+      <link rel="stylesheet" href="$relativePath/style/module.css" type="text/css" />]]>
+    </head>
+
     <menu name="Numbers">
       <item name="Overview" href="/index.html"/>
       <item name="Downloads" href="http://commons.apache.org/numbers/download_numbers.cgi"/>
@@ -34,15 +40,22 @@
       <item name="Developers Guide" href="/developers.html"/>
     </menu>
 
-    <!-- <menu name="User Guide">
+    <menu name="User Guide">
       <item name="Contents" href="/userguide/index.html"/>
-    </menu> -->
+    </menu>
+  </body>
 
-    <head>
-      <![CDATA[<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
-      </script>
-      <link rel="stylesheet" href="$relativePath/style/module.css" type="text/css" />]]>
-    </head>
+  <custom>
+    <!-- Custom properties controlling the commons-skin template. -->
+    <commonsSkin>
+      <!-- 
+        Convert preformatted source section tags to prettyprint:
+          <div class="source"><pre> to <div class="source"><pre class="prettyprint">
+      -->
+      <prettyPrintSourcePreTags>true</prettyPrintSourcePreTags>
 
-  </body>
+      <!-- Add the "linenums" class to the prettyprint enabled source tags -->
+      <prettyPrintLineNumbersEnabled>false</prettyPrintLineNumbersEnabled>
+    </commonsSkin>
+  </custom>
 </project>
diff --git a/src/site/xdoc/userguide/core.xml b/src/site/xdoc/userguide/core.xml
deleted file mode 100644
index e9dcee83..00000000
--- a/src/site/xdoc/userguide/core.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0"?>
-
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-  -->
-
-<document>
-  <properties>
-    <title>User Guide - Module "core"</title>
-  </properties>
-
-<body>
-
-<section name="Core">
-
-<subsection name="1.1 Section" href="section">
-    <p>
-    Contribution welcome...
-    </p>
-</subsection>
-
-</section>
-
-</body>
-</document>
diff --git a/src/site/xdoc/userguide/index.xml b/src/site/xdoc/userguide/index.xml
deleted file mode 100644
index 726df01d..00000000
--- a/src/site/xdoc/userguide/index.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0"?>
-
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-  -->
-
-<document url="index.html">
-  <properties>
-    <title>The Commons Numbers User Guide - Table of Contents</title>
-  </properties>
-
-  <body>
-      <section name="Table of Contents" href="toc">
-          <ul>
-              <li>
-                  <a href="core.html">1. Core</a>
-              </li>
-          </ul>
-      </section>
-  </body>
-
-</document>
diff --git a/src/site/xdoc/userguide/xdoc.xsl b/src/site/xdoc/userguide/xdoc.xsl
deleted file mode 100644
index cfbd5f52..00000000
--- a/src/site/xdoc/userguide/xdoc.xsl
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<!--
-   Licensed to the Apache Software Foundation (ASF) under one or more
-  contributor license agreements.  See the NOTICE file distributed with
-  this work for additional information regarding copyright ownership.
-  The ASF licenses this file to You under the Apache License, Version 2.0
-  (the "License"); you may not use this file except in compliance with
-  the License.  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-  -->
-
-<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
-    <xsl:template match="/">
-        <html xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-            <head>
-                <xsl:apply-templates select="/document/properties/title"/>
-                <xsl:apply-templates select="/document/meta"/>
-                <style type="text/css">
-                    @import url("../style/tigris.css");
-                    @import url("../style/maven.css");
-                    @import url("../style/project.css");
-                </style>
-                <link rel="stylesheet" href="../style/print.css" type="text/css" media="print"></link>
-            </head>
-            <xsl:apply-templates select="/document/body"/>
-        </html>
-    </xsl:template>
-    <xsl:template match="body">
-        <body>
-            <div class="app">
-                <xsl:apply-templates/>
-            </div>
-        </body> 
-    </xsl:template>
-    <xsl:template match="section">
-        <div>
-            <h3><xsl:value-of select="@name"/></h3>
-            <xsl:apply-templates/>
-        </div>
-    </xsl:template>
-    <xsl:template match="subsection">
-        <div>
-            <h4><xsl:value-of select="@name"/></h4>
-            <xsl:apply-templates/>
-        </div>
-    </xsl:template>
-    <xsl:template match="source">
-        <div id="source">
-            <pre>
-                <xsl:apply-templates/>
-            </pre>
-        </div>
-    </xsl:template> 
-    <xsl:template match="node()|@*">
-        <xsl:copy>
-            <xsl:apply-templates select="@*"/>
-            <xsl:apply-templates/>
-        </xsl:copy>
-    </xsl:template>
-</xsl:stylesheet>