Merge branch 'master' of https://gitbox.apache.org/repos/asf/commons-numbers
diff --git a/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/ArithmeticUtils.java b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/ArithmeticUtils.java
index b2bb256..bbd8292 100644
--- a/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/ArithmeticUtils.java
+++ b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/ArithmeticUtils.java
@@ -266,6 +266,16 @@
     /**
      * Raise an int to an int power.
      *
+     * <p>Special cases:</p>
+     * <ul>
+     *   <li>{@code k^0} returns {@code 1} (including {@code k=0})
+     *   <li>{@code k^1} returns {@code k} (including {@code k=0})
+     *   <li>{@code 0^0} returns {@code 1}
+     *   <li>{@code 0^e} returns {@code 0}
+     *   <li>{@code 1^e} returns {@code 1}
+     *   <li>{@code (-1)^e} returns {@code -1 or 1} if {@code e} is odd or even
+     * </ul>
+     *
      * @param k Number to raise.
      * @param e Exponent (must be positive or zero).
      * @return \( k^e \)
@@ -278,6 +288,22 @@
             throw new IllegalArgumentException(NEGATIVE_EXPONENT_1 + e + NEGATIVE_EXPONENT_2);
         }
 
+        if (k == 0) {
+            return e == 0 ? 1 : 0;
+        }
+
+        if (k == 1) {
+            return 1;
+        }
+
+        if (k == -1) {
+            return (e & 1) == 0 ? 1 : -1;
+        }
+
+        if (e >= 31) {
+            throw new ArithmeticException("integer overflow");
+        }
+
         int exp = e;
         int result = 1;
         int k2p    = k;
@@ -300,6 +326,16 @@
     /**
      * Raise a long to an int power.
      *
+     * <p>Special cases:</p>
+     * <ul>
+     *   <li>{@code k^0} returns {@code 1} (including {@code k=0})
+     *   <li>{@code k^1} returns {@code k} (including {@code k=0})
+     *   <li>{@code 0^0} returns {@code 1}
+     *   <li>{@code 0^e} returns {@code 0}
+     *   <li>{@code 1^e} returns {@code 1}
+     *   <li>{@code (-1)^e} returns {@code -1 or 1} if {@code e} is odd or even
+     * </ul>
+     *
      * @param k Number to raise.
      * @param e Exponent (must be positive or zero).
      * @return \( k^e \)
@@ -312,6 +348,22 @@
             throw new IllegalArgumentException(NEGATIVE_EXPONENT_1 + e + NEGATIVE_EXPONENT_2);
         }
 
+        if (k == 0L) {
+            return e == 0 ? 1L : 0L;
+        }
+
+        if (k == 1L) {
+            return 1L;
+        }
+
+        if (k == -1L) {
+            return (e & 1) == 0 ? 1L : -1L;
+        }
+
+        if (e >= 63) {
+            throw new ArithmeticException("long overflow");
+        }
+
         int exp = e;
         long result = 1;
         long k2p    = k;
diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/ArithmeticUtilsTest.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/ArithmeticUtilsTest.java
index 5a4bb73..5f466a2 100644
--- a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/ArithmeticUtilsTest.java
+++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/ArithmeticUtilsTest.java
@@ -422,6 +422,25 @@
     }
 
     @Test
+    void testPowEdgeCases() {
+        Assertions.assertEquals(0, ArithmeticUtils.pow(0, 2));
+        Assertions.assertEquals(0L, ArithmeticUtils.pow(0L, 2));
+        Assertions.assertEquals(0, ArithmeticUtils.pow(0, 1));
+        Assertions.assertEquals(0L, ArithmeticUtils.pow(0L, 1));
+        Assertions.assertEquals(1, ArithmeticUtils.pow(0, 0));
+        Assertions.assertEquals(1L, ArithmeticUtils.pow(0L, 0));
+
+        for (int i = 20; i <= 35; i++) {
+            final int ti = i;
+            Assertions.assertThrows(ArithmeticException.class, () -> ArithmeticUtils.pow(3, ti));
+        }
+        for (int i = 40; i <= 70; i++) {
+            final int ti = i;
+            Assertions.assertThrows(ArithmeticException.class, () -> ArithmeticUtils.pow(3L, ti));
+        }
+    }
+
+    @Test
     void testIsPowerOfTwo() {
         final int n = 1025;
         final boolean[] expected = new boolean[n];
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java
index a172d44..bc71783 100644
--- a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java
@@ -966,23 +966,36 @@
      */
     @Override
     public BigFraction pow(final int exponent) {
+        if (exponent == 1) {
+            return this;
+        }
         if (exponent == 0) {
             return ONE;
         }
         if (isZero()) {
+            if (exponent < 0) {
+                throw new FractionException(FractionException.ERROR_ZERO_DENOMINATOR);
+            }
             return ZERO;
         }
-
+        if (exponent > 0) {
+            return new BigFraction(numerator.pow(exponent),
+                                   denominator.pow(exponent));
+        }
+        if (exponent == -1) {
+            return this.reciprocal();
+        }
+        if (exponent == Integer.MIN_VALUE) {
+            // MIN_VALUE can't be negated
+            return new BigFraction(denominator.pow(Integer.MAX_VALUE).multiply(denominator),
+                                   numerator.pow(Integer.MAX_VALUE).multiply(numerator));
+        }
         // Note: Raise the BigIntegers to the power and then reduce.
         // The supported range for BigInteger is currently
         // +/-2^(Integer.MAX_VALUE) exclusive thus larger
         // exponents (long, BigInteger) are currently not supported.
-        if (exponent < 0) {
-            return new BigFraction(denominator.pow(-exponent),
-                                   numerator.pow(-exponent));
-        }
-        return new BigFraction(numerator.pow(exponent),
-                               denominator.pow(exponent));
+        return new BigFraction(denominator.pow(-exponent),
+                               numerator.pow(-exponent));
     }
 
     /**
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java
index f1bc542..2afe738 100644
--- a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/Fraction.java
@@ -775,19 +775,32 @@
      */
     @Override
     public Fraction pow(final int exponent) {
+        if (exponent == 1) {
+            return this;
+        }
         if (exponent == 0) {
             return ONE;
         }
         if (isZero()) {
+            if (exponent < 0) {
+                throw new FractionException(FractionException.ERROR_ZERO_DENOMINATOR);
+            }
             return ZERO;
         }
-
-        if (exponent < 0) {
-            return new Fraction(ArithmeticUtils.pow(denominator, -exponent),
-                                ArithmeticUtils.pow(numerator,   -exponent));
+        if (exponent > 0) {
+            return new Fraction(ArithmeticUtils.pow(numerator, exponent),
+                                ArithmeticUtils.pow(denominator, exponent));
         }
-        return new Fraction(ArithmeticUtils.pow(numerator,   exponent),
-                            ArithmeticUtils.pow(denominator, exponent));
+        if (exponent == -1) {
+            return this.reciprocal();
+        }
+        if (exponent == Integer.MIN_VALUE) {
+            // MIN_VALUE can't be negated
+            return new Fraction(ArithmeticUtils.pow(denominator, Integer.MAX_VALUE) * denominator,
+                                ArithmeticUtils.pow(numerator, Integer.MAX_VALUE) * numerator);
+        }
+        return new Fraction(ArithmeticUtils.pow(denominator, -exponent),
+                            ArithmeticUtils.pow(numerator, -exponent));
     }
 
     /**
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
index 5ded68e..f6c147f 100644
--- a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/BigFractionTest.java
@@ -778,4 +778,15 @@
                                                    fractionA.getDenominator().multiply(fractionB.getDenominator()));
         Assertions.assertEquals(correctResult, errorResult);
     }
+
+    @Test
+    void testNumbers150() {
+        // zero to negative powers should throw an exception
+        Assertions.assertThrows(ArithmeticException.class, () -> BigFraction.ZERO.pow(-1));
+        Assertions.assertThrows(ArithmeticException.class, () -> BigFraction.ZERO.pow(Integer.MIN_VALUE));
+
+        // shall overflow
+        Assertions.assertThrows(ArithmeticException.class, () -> BigFraction.of(2).pow(Integer.MIN_VALUE));
+        Assertions.assertThrows(ArithmeticException.class, () -> BigFraction.of(1, 2).pow(Integer.MIN_VALUE));
+    }
 }
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonTestCases.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonTestCases.java
index d6e58dc..621d02a 100644
--- a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonTestCases.java
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonTestCases.java
@@ -570,6 +570,10 @@
         testCases.add(new BinaryIntOperatorTestCase(0, 1, Integer.MAX_VALUE, 0, 1));
         testCases.add(new BinaryIntOperatorTestCase(0, -1, Integer.MAX_VALUE, 0, 1));
 
+        testCases.add(new BinaryIntOperatorTestCase(1, 1, Integer.MIN_VALUE, 1, 1));
+        testCases.add(new BinaryIntOperatorTestCase(1, -1, Integer.MIN_VALUE, 1, 1));
+        testCases.add(new BinaryIntOperatorTestCase(-1, 1, Integer.MIN_VALUE, 1, 1));
+        testCases.add(new BinaryIntOperatorTestCase(-1, -1, Integer.MIN_VALUE, 1, 1));
         return testCases;
     }
 
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonsLangPortedFractionTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonsLangPortedFractionTest.java
new file mode 100644
index 0000000..892a6ba
--- /dev/null
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/CommonsLangPortedFractionTest.java
@@ -0,0 +1,1161 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Test cases for the {@link Fraction} class.
+ *
+ * <p>This class is ported from commons lang to demonstrate interoperability of
+ * the Fraction class in numbers.</p>
+ */
+public class CommonsLangPortedFractionTest {
+
+    private static final int SKIP = 500;  //53
+
+    //--------------------------------------------------------------------------
+    @Test
+    public void testConstants() {
+        assertEquals(0, Fraction.ZERO.getNumerator());
+        assertEquals(1, Fraction.ZERO.getDenominator());
+
+        assertEquals(1, Fraction.ONE.getNumerator());
+        assertEquals(1, Fraction.ONE.getDenominator());
+
+        /*
+         *  All these constants need not be supported.
+         *  Users can create whatever constants they require.
+         *  The special constants ZERO and ONE are for the additive and multiplicative identity.
+         *
+         *  assertEquals(1, Fraction.ONE_HALF.getNumerator());
+         *  assertEquals(2, Fraction.ONE_HALF.getDenominator());
+         *
+         *  assertEquals(1, Fraction.ONE_THIRD.getNumerator());
+         *  assertEquals(3, Fraction.ONE_THIRD.getDenominator());
+         *
+         *  assertEquals(2, Fraction.TWO_THIRDS.getNumerator());
+         *  assertEquals(3, Fraction.TWO_THIRDS.getDenominator());
+         *
+         *  assertEquals(1, Fraction.ONE_QUARTER.getNumerator());
+         *  assertEquals(4, Fraction.ONE_QUARTER.getDenominator());
+         *
+         *  assertEquals(1, Fraction.TWO_QUARTERS.getNumerator());
+         *  assertEquals(2, Fraction.TWO_QUARTERS.getDenominator());
+         *
+         *  assertEquals(3, Fraction.THREE_QUARTERS.getNumerator());
+         *  assertEquals(4, Fraction.THREE_QUARTERS.getDenominator());
+         *
+         *  assertEquals(1, Fraction.ONE_FIFTH.getNumerator());
+         *  assertEquals(5, Fraction.ONE_FIFTH.getDenominator());
+         *
+         *  assertEquals(2, Fraction.TWO_FIFTHS.getNumerator());
+         *  assertEquals(5, Fraction.TWO_FIFTHS.getDenominator());
+         *
+         *  assertEquals(3, Fraction.THREE_FIFTHS.getNumerator());
+         *  assertEquals(5, Fraction.THREE_FIFTHS.getDenominator());
+         *
+         *  assertEquals(4, Fraction.FOUR_FIFTHS.getNumerator());
+         *  assertEquals(5, Fraction.FOUR_FIFTHS.getDenominator());
+         */
+    }
+
+    @Test
+    public void testFactory_int_int() {
+        Fraction f = null;
+
+        // zero
+        f = Fraction.of(0, 1);
+        assertEquals(0, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f = Fraction.of(0, 2);
+        assertEquals(0, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        // normal
+        f = Fraction.of(1, 1);
+        assertEquals(1, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f = Fraction.of(2, 1);
+        assertEquals(2, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f = Fraction.of(23, 345);
+        assertEquals(1, f.getNumerator());
+        assertEquals(15, f.getDenominator());
+
+        // improper
+        f = Fraction.of(22, 7);
+        assertEquals(22, f.getNumerator());
+        assertEquals(7, f.getDenominator());
+
+        // negatives
+        f = Fraction.of(-6, 10);
+        assertEquals(-3, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f = Fraction.of(6, -10);
+        assertEquals(3, f.getNumerator());
+        assertEquals(-5, f.getDenominator());
+
+        f = Fraction.of(-6, -10);
+        assertEquals(-3, f.getNumerator());
+        assertEquals(-5, f.getDenominator());
+
+        // zero denominator
+        assertThrows(ArithmeticException.class, () -> Fraction.of(1, 0));
+        assertThrows(ArithmeticException.class, () -> Fraction.of(2, 0));
+        assertThrows(ArithmeticException.class, () -> Fraction.of(-3, 0));
+
+        // lang cannot represent the unsimplified fraction with MIN_VALUE as the denominator
+        // assertThrows(ArithmeticException.class, () -> Fraction.getFraction(4, Integer.MIN_VALUE));
+        // assertThrows(ArithmeticException.class, () -> Fraction.getFraction(1, Integer.MIN_VALUE));
+        // numbers will always simplify the fraction
+        f = Fraction.of(4, Integer.MIN_VALUE);
+        assertEquals(-1, f.signum());
+        assertEquals(1, f.getNumerator());
+        assertEquals(Integer.MIN_VALUE / 4, f.getDenominator());
+        // numbers can use MIN_VALUE as the denominator
+        f = Fraction.of(1, Integer.MIN_VALUE);
+        assertEquals(-1, f.signum());
+        assertEquals(1, f.getNumerator());
+        assertEquals(Integer.MIN_VALUE, f.getDenominator());
+    }
+
+/*
+ *  Removed as not supported in numbers.
+ *
+ *  @Test
+ *  public void testFactory_int_int_int() {
+ *      Fraction f = null;
+ *
+ *      // zero
+ *      f = Fraction.of(0, 0, 2);
+ *      assertEquals(0, f.getNumerator());
+ *      assertEquals(1, f.getDenominator());
+ *
+ *      f = Fraction.of(2, 0, 2);
+ *      assertEquals(2, f.getNumerator());
+ *      assertEquals(1, f.getDenominator());
+ *
+ *      f = Fraction.of(0, 1, 2);
+ *      assertEquals(1, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      // normal
+ *      f = Fraction.of(1, 1, 2);
+ *      assertEquals(3, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      // negatives
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(1, -6, -10));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(1, -6, -10));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(1, -6, -10));
+ *
+ *      // negative whole
+ *      f = Fraction.of(-1, 6, 10);
+ *      assertEquals(-8, f.getNumerator());
+ *      assertEquals(5, f.getDenominator());
+ *
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(-1, -6, 10));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(-1, 6, -10));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(-1, -6, -10));
+ *
+ *      // zero denominator
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(0, 1, 0));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(1, 2, 0));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(-1, -3, 0));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(Integer.MAX_VALUE, 1, 2));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(-Integer.MAX_VALUE, 1, 2));
+ *
+ *      // very large
+ *      f = Fraction.of(-1, 0, Integer.MAX_VALUE);
+ *      assertEquals(-1, f.getNumerator());
+ *      assertEquals(1, f.getDenominator());
+ *
+ *      // negative denominators not allowed in this constructor.
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(0, 4, Integer.MIN_VALUE));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(1, 1, Integer.MAX_VALUE));
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(-1, 2, Integer.MAX_VALUE));
+ *  }
+ */
+
+    @Test
+    public void testReducedFactory_int_int() {
+        Fraction f = null;
+
+        // zero
+        f = Fraction.of(0, 1);
+        assertEquals(0, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        // normal
+        f = Fraction.of(1, 1);
+        assertEquals(1, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f = Fraction.of(2, 1);
+        assertEquals(2, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        // improper
+        f = Fraction.of(22, 7);
+        assertEquals(22, f.getNumerator());
+        assertEquals(7, f.getDenominator());
+
+        // negatives
+        f = Fraction.of(-6, 10);
+        assertEquals(-3, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f = Fraction.of(6, -10);
+        assertEquals(3, f.getNumerator());
+        assertEquals(-5, f.getDenominator());
+
+        f = Fraction.of(-6, -10);
+        assertEquals(-3, f.getNumerator());
+        assertEquals(-5, f.getDenominator());
+
+        // zero denominator
+        assertThrows(ArithmeticException.class, () -> Fraction.of(1, 0));
+        assertThrows(ArithmeticException.class, () -> Fraction.of(2, 0));
+        assertThrows(ArithmeticException.class, () -> Fraction.of(-3, 0));
+
+        // reduced
+        f = Fraction.of(0, 2);
+        assertEquals(0, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f = Fraction.of(2, 2);
+        assertEquals(1, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f = Fraction.of(2, 4);
+        assertEquals(1, f.getNumerator());
+        assertEquals(2, f.getDenominator());
+
+        f = Fraction.of(15, 10);
+        assertEquals(3, f.getNumerator());
+        assertEquals(2, f.getDenominator());
+
+        f = Fraction.of(121, 22);
+        assertEquals(11, f.getNumerator());
+        assertEquals(2, f.getDenominator());
+
+        // Extreme values
+        // OK, can reduce before negating
+        f = Fraction.of(-2, Integer.MIN_VALUE);
+        assertEquals(-1, f.getNumerator());
+        assertEquals(Integer.MIN_VALUE / 2, f.getDenominator());
+
+        // lang requires the sign to be in the numerator so this would throw.
+        // assertThrows(ArithmeticException.class, () -> Fraction.getReducedFraction(-7, Integer.MIN_VALUE));
+        // numbers allows the sign to be in the denominator so this does not throw.
+        f = Fraction.of(-7, Integer.MIN_VALUE);
+        assertEquals(1, f.signum());
+        assertEquals(-7, f.getNumerator());
+        assertEquals(Integer.MIN_VALUE, f.getDenominator());
+
+        // LANG-662
+        f = Fraction.of(Integer.MIN_VALUE, 2);
+        assertEquals(Integer.MIN_VALUE / 2, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+    }
+
+    @Test
+    public void testFactory_double() {
+        assertThrows(IllegalArgumentException.class, () -> Fraction.from(Double.NaN));
+        assertThrows(IllegalArgumentException.class, () -> Fraction.from(Double.POSITIVE_INFINITY));
+        assertThrows(IllegalArgumentException.class, () -> Fraction.from(Double.NEGATIVE_INFINITY));
+        Fraction.from((double) Integer.MAX_VALUE + 1);
+
+        // zero
+        Fraction f = Fraction.from(0.0d);
+        assertEquals(0, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        // one
+        f = Fraction.from(1.0d);
+        assertEquals(1, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        // one half
+        f = Fraction.from(0.5d);
+        assertEquals(1, f.getNumerator());
+        assertEquals(2, f.getDenominator());
+
+        // negative
+        f = Fraction.from(-0.875d);
+        assertEquals(-7, f.getNumerator());
+        assertEquals(8, f.getDenominator());
+
+        // over 1
+        f = Fraction.from(1.25d);
+        assertEquals(5, f.getNumerator());
+        assertEquals(4, f.getDenominator());
+
+        // two thirds
+        f = Fraction.from(0.66666d);
+        assertEquals(2, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        // small
+        f = Fraction.from(1.0d / 10001d);
+        assertEquals(1, f.getNumerator());
+        assertEquals(10001, f.getDenominator());
+
+        // normal
+        Fraction f2 = null;
+        for (int i = 1; i <= 100; i++) {  // denominator
+            for (int j = 1; j <= i; j++) {  // numerator
+                f = Fraction.from((double) j / (double) i);
+
+                f2 = Fraction.of(j, i);
+                assertEquals(f2.getNumerator(), f.getNumerator());
+                assertEquals(f2.getDenominator(), f.getDenominator());
+            }
+        }
+        // save time by skipping some tests!  (
+        for (int i = 1001; i <= 10000; i += SKIP) {  // denominator
+            for (int j = 1; j <= i; j++) {  // numerator
+                f = Fraction.from((double) j / (double) i, 1e-8, 100);
+                f2 = Fraction.of(j, i);
+                assertEquals(f2.getNumerator(), f.getNumerator());
+                assertEquals(f2.getDenominator(), f.getDenominator());
+            }
+        }
+    }
+
+/*
+ *  Removed as not supported in numbers.
+ *
+ *  @Test
+ *  public void testFactory_String() {
+ *      assertThrows(NullPointerException.class, () -> Fraction.from(null));
+ *  }
+ *
+ *
+ *  @Test
+ *  public void testFactory_String_double() {
+ *      Fraction f = null;
+ *
+ *      f = Fraction.from("0.0");
+ *      assertEquals(0, f.getNumerator());
+ *      assertEquals(1, f.getDenominator());
+ *
+ *      f = Fraction.from("0.2");
+ *      assertEquals(1, f.getNumerator());
+ *      assertEquals(5, f.getDenominator());
+ *
+ *      f = Fraction.from("0.5");
+ *      assertEquals(1, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      f = Fraction.from("0.66666");
+ *      assertEquals(2, f.getNumerator());
+ *      assertEquals(3, f.getDenominator());
+ *
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("2.3R"));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("2147483648")); // too big
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("."));
+ *  }
+ *
+ *  @Test
+ *  public void testFactory_String_proper() {
+ *      Fraction f = null;
+ *
+ *      f = Fraction.from("0 0/1");
+ *      assertEquals(0, f.getNumerator());
+ *      assertEquals(1, f.getDenominator());
+ *
+ *      f = Fraction.from("1 1/5");
+ *      assertEquals(6, f.getNumerator());
+ *      assertEquals(5, f.getDenominator());
+ *
+ *      f = Fraction.from("7 1/2");
+ *      assertEquals(15, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      f = Fraction.from("1 2/4");
+ *      assertEquals(3, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      f = Fraction.from("-7 1/2");
+ *      assertEquals(-15, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      f = Fraction.from("-1 2/4");
+ *      assertEquals(-3, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("2 3"));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("a 3"));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("2 b/4"));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("2 "));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from(" 3"));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from(" "));
+ *  }
+ *
+ *  @Test
+ *  public void testFactory_String_improper() {
+ *      Fraction f = null;
+ *
+ *      f = Fraction.from("0/1");
+ *      assertEquals(0, f.getNumerator());
+ *      assertEquals(1, f.getDenominator());
+ *
+ *      f = Fraction.from("1/5");
+ *      assertEquals(1, f.getNumerator());
+ *      assertEquals(5, f.getDenominator());
+ *
+ *      f = Fraction.from("1/2");
+ *      assertEquals(1, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      f = Fraction.from("2/3");
+ *      assertEquals(2, f.getNumerator());
+ *      assertEquals(3, f.getDenominator());
+ *
+ *      f = Fraction.from("7/3");
+ *      assertEquals(7, f.getNumerator());
+ *      assertEquals(3, f.getDenominator());
+ *
+ *      f = Fraction.from("2/4");
+ *      assertEquals(1, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("2/d"));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("2e/3"));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("2/"));
+ *      assertThrows(NumberFormatException.class, () -> Fraction.from("/"));
+ *  }
+ *
+ *  @Test
+ *  public void testGets() {
+ *      Fraction f = null;
+ *
+ *      f = Fraction.of(3, 5, 6);
+ *      assertEquals(23, f.getNumerator());
+ *      assertEquals(3, f.getProperWhole());
+ *      assertEquals(5, f.getProperNumerator());
+ *      assertEquals(6, f.getDenominator());
+ *
+ *      f = Fraction.of(-3, 5, 6);
+ *      assertEquals(-23, f.getNumerator());
+ *      assertEquals(-3, f.getProperWhole());
+ *      assertEquals(5, f.getProperNumerator());
+ *      assertEquals(6, f.getDenominator());
+ *
+ *      f = Fraction.of(Integer.MIN_VALUE, 0, 1);
+ *      assertEquals(Integer.MIN_VALUE, f.getNumerator());
+ *      assertEquals(Integer.MIN_VALUE, f.getProperWhole());
+ *      assertEquals(0, f.getProperNumerator());
+ *      assertEquals(1, f.getDenominator());
+ *  }
+ *
+ *  @Test
+ *  public void testConversions() {
+ *      Fraction f = null;
+ *
+ *      f = Fraction.of(3, 7, 8);
+ *      assertEquals(3, f.intValue());
+ *      assertEquals(3L, f.longValue());
+ *      assertEquals(3.875f, f.floatValue(), 0.00001f);
+ *      assertEquals(3.875d, f.doubleValue(), 0.00001d);
+ *  }
+ *
+ *  @Test
+ *  public void testReduce() {
+ *      Fraction f = null;
+ *
+ *      f = Fraction.of(50, 75);
+ *      Fraction result = f.reduce();
+ *      assertEquals(2, result.getNumerator());
+ *      assertEquals(3, result.getDenominator());
+ *
+ *      f = Fraction.of(-2, -3);
+ *      result = f.reduce();
+ *      assertEquals(2, result.getNumerator());
+ *      assertEquals(3, result.getDenominator());
+ *
+ *      f = Fraction.of(2, -3);
+ *      result = f.reduce();
+ *      assertEquals(-2, result.getNumerator());
+ *      assertEquals(3, result.getDenominator());
+ *
+ *      f = Fraction.of(-2, 3);
+ *      result = f.reduce();
+ *      assertEquals(-2, result.getNumerator());
+ *      assertEquals(3, result.getDenominator());
+ *      assertSame(f, result);
+ *
+ *      f = Fraction.of(2, 3);
+ *      result = f.reduce();
+ *      assertEquals(2, result.getNumerator());
+ *      assertEquals(3, result.getDenominator());
+ *      assertSame(f, result);
+ *
+ *      f = Fraction.of(0, 1);
+ *      result = f.reduce();
+ *      assertEquals(0, result.getNumerator());
+ *      assertEquals(1, result.getDenominator());
+ *      assertSame(f, result);
+ *
+ *      f = Fraction.of(0, 100);
+ *      result = f.reduce();
+ *      assertEquals(0, result.getNumerator());
+ *      assertEquals(1, result.getDenominator());
+ *      assertSame(result, Fraction.ZERO);
+ *
+ *      f = Fraction.of(Integer.MIN_VALUE, 2);
+ *      result = f.reduce();
+ *      assertEquals(Integer.MIN_VALUE / 2, result.getNumerator());
+ *      assertEquals(1, result.getDenominator());
+ *  }
+ *
+ *  @Test
+ *  public void testreciprocal() {
+ *      Fraction f = null;
+ *
+ *      f = Fraction.of(50, 75);
+ *      f = f.reciprocal();
+ *      assertEquals(3, f.getNumerator());
+ *      assertEquals(2, f.getDenominator());
+ *
+ *      f = Fraction.of(4, 3);
+ *      f = f.reciprocal();
+ *      assertEquals(3, f.getNumerator());
+ *      assertEquals(4, f.getDenominator());
+ *
+ *      f = Fraction.of(-15, 47);
+ *      f = f.reciprocal();
+ *      assertEquals(47, f.getNumerator());
+ *      assertEquals(-15, f.getDenominator());
+ *
+ *      assertThrows(ArithmeticException.class, () -> Fraction.of(0, 3).reciprocal());
+ *      Fraction.of(Integer.MIN_VALUE, 1).reciprocal();
+ *
+ *      f = Fraction.of(Integer.MAX_VALUE, 1);
+ *      f = f.reciprocal();
+ *      assertEquals(1, f.getNumerator());
+ *      assertEquals(Integer.MAX_VALUE, f.getDenominator());
+ *  }
+ */
+
+    @Test
+    public void testNegate() {
+        Fraction f = null;
+
+        f = Fraction.of(50, 75);
+        f = f.negate();
+        assertEquals(-2, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        f = Fraction.of(-50, 75);
+        f = f.negate();
+        assertEquals(2, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        // large values
+        f = Fraction.of(Integer.MAX_VALUE - 1, Integer.MAX_VALUE);
+        f = f.negate();
+        assertEquals(Integer.MIN_VALUE + 2, f.getNumerator());
+        assertEquals(Integer.MAX_VALUE, f.getDenominator());
+
+        // lang requires the sign in the numerator and so cannot negate MIN_VALUE as the numerator
+        // assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 1).negate());
+        // numbers allows the sign in the numerator or denominator
+        f = Fraction.of(Integer.MIN_VALUE, 1).negate();
+        assertEquals(1, f.signum());
+        assertEquals(Integer.MIN_VALUE, f.getNumerator());
+        assertEquals(-1, f.getDenominator());
+    }
+
+    @Test
+    public void testAbs() {
+        Fraction f = null;
+
+        f = Fraction.of(50, 75);
+        f = f.abs();
+        assertEquals(2, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        f = Fraction.of(-50, 75);
+        f = f.abs();
+        assertEquals(2, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        f = Fraction.of(Integer.MAX_VALUE, 1);
+        f = f.abs();
+        assertEquals(Integer.MAX_VALUE, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f = Fraction.of(Integer.MAX_VALUE, -1);
+        f = f.abs();
+        assertEquals(-Integer.MAX_VALUE, f.getNumerator());
+        assertEquals(-1, f.getDenominator());
+
+        // lang requires the sign in the numerator and so cannot compute the absolute with MIN_VALUE as the numerator
+        // assertThrows(ArithmeticException.class, () -> Fraction.getFraction(Integer.MIN_VALUE, 1).abs());
+        // numbers allows the sign in the numerator or denominator
+        f = Fraction.of(Integer.MIN_VALUE, 1).abs();
+        assertEquals(1, f.signum());
+        assertEquals(Integer.MIN_VALUE, f.getNumerator());
+        assertEquals(-1, f.getDenominator());
+    }
+
+    @Test
+    public void testPow() {
+        Fraction f = null;
+
+        f = Fraction.of(3, 5);
+        assertEquals(Fraction.ONE, f.pow(0));
+
+        f = Fraction.of(3, 5);
+        assertEquals(f, f.pow(1));
+        assertEquals(f, f.pow(1));
+
+        f = Fraction.of(3, 5);
+        f = f.pow(2);
+        assertEquals(9, f.getNumerator());
+        assertEquals(25, f.getDenominator());
+
+        f = Fraction.of(3, 5);
+        f = f.pow(3);
+        assertEquals(27, f.getNumerator());
+        assertEquals(125, f.getDenominator());
+
+        f = Fraction.of(3, 5);
+        f = f.pow(-1);
+        assertEquals(5, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        f = Fraction.of(3, 5);
+        f = f.pow(-2);
+        assertEquals(25, f.getNumerator());
+        assertEquals(9, f.getDenominator());
+
+        // check unreduced fractions stay that way.
+        f = Fraction.of(6, 10);
+        assertEquals(Fraction.ONE, f.pow(0));
+
+        f = Fraction.of(6, 10);
+        assertEquals(f, f.pow(1));
+        assertEquals(f.pow(1), Fraction.of(3, 5));
+
+        f = Fraction.of(6, 10);
+        f = f.pow(2);
+        assertEquals(9, f.getNumerator());
+        assertEquals(25, f.getDenominator());
+
+        f = Fraction.of(6, 10);
+        f = f.pow(3);
+        assertEquals(27, f.getNumerator());
+        assertEquals(125, f.getDenominator());
+
+        f = Fraction.of(6, 10);
+        f = f.pow(-1);
+        assertEquals(5, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        f = Fraction.of(6, 10);
+        f = f.pow(-2);
+        assertEquals(25, f.getNumerator());
+        assertEquals(9, f.getDenominator());
+
+        // zero to any positive power is still zero.
+        f = Fraction.of(0, 1231);
+        f = f.pow(1);
+        assertEquals(0, f.compareTo(Fraction.ZERO));
+        assertEquals(0, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+        f = f.pow(2);
+        assertEquals(0, f.compareTo(Fraction.ZERO));
+        assertEquals(0, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        // zero to negative powers should throw an exception
+        final Fraction fr = f;
+        assertThrows(ArithmeticException.class, () -> fr.pow(-1));
+        assertThrows(ArithmeticException.class, () -> fr.pow(Integer.MIN_VALUE));
+
+        // one to any power is still one.
+        f = Fraction.of(1, 1);
+        f = f.pow(0);
+        assertEquals(f, Fraction.ONE);
+        f = f.pow(1);
+        assertEquals(f, Fraction.ONE);
+        f = f.pow(-1);
+        assertEquals(f, Fraction.ONE);
+        f = f.pow(Integer.MAX_VALUE);
+        assertEquals(f, Fraction.ONE);
+        f = f.pow(Integer.MIN_VALUE);
+        assertEquals(f, Fraction.ONE);
+
+        assertThrows(ArithmeticException.class, () -> Fraction.of(Integer.MAX_VALUE, 1).pow(2));
+
+        // Numerator growing too negative during the pow operation.
+        assertThrows(ArithmeticException.class, () -> Fraction.of(Integer.MIN_VALUE, 1).pow(3));
+
+        assertThrows(ArithmeticException.class, () -> Fraction.of(65536, 1).pow(2));
+    }
+
+    @Test
+    public void testAdd() {
+        Fraction f = null;
+        Fraction f1 = null;
+        Fraction f2 = null;
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(1, 5);
+        f = f1.add(f2);
+        assertEquals(4, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(2, 5);
+        f = f1.add(f2);
+        assertEquals(1, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(3, 5);
+        f = f1.add(f2);
+        assertEquals(6, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(-4, 5);
+        f = f1.add(f2);
+        assertEquals(-1, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f1 = Fraction.of(Integer.MAX_VALUE - 1, 1);
+        f2 = Fraction.ONE;
+        f = f1.add(f2);
+        assertEquals(Integer.MAX_VALUE, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(1, 2);
+        f = f1.add(f2);
+        assertEquals(11, f.getNumerator());
+        assertEquals(10, f.getDenominator());
+
+        f1 = Fraction.of(3, 8);
+        f2 = Fraction.of(1, 6);
+        f = f1.add(f2);
+        assertEquals(13, f.getNumerator());
+        assertEquals(24, f.getDenominator());
+
+        f1 = Fraction.of(0, 5);
+        f2 = Fraction.of(1, 5);
+        f = f1.add(f2);
+        assertSame(f2, f);
+        f = f2.add(f1);
+        assertSame(f2, f);
+
+        f1 = Fraction.of(-1, 13 * 13 * 2 * 2);
+        f2 = Fraction.of(-2, 13 * 17 * 2);
+        final Fraction fr = f1.add(f2);
+        assertEquals(13 * 13 * 17 * 2 * 2, fr.getDenominator());
+        assertEquals(-17 - 2 * 13 * 2, fr.getNumerator());
+
+        assertThrows(NullPointerException.class, () -> fr.add(null));
+
+        // if this fraction is added naively, it will overflow.
+        // check that it doesn't.
+        f1 = Fraction.of(1, 32768 * 3);
+        f2 = Fraction.of(1, 59049);
+        f = f1.add(f2);
+        assertEquals(52451, f.getNumerator());
+        assertEquals(1934917632, f.getDenominator());
+
+        f1 = Fraction.of(Integer.MIN_VALUE, 3);
+        f2 = Fraction.of(1, 3);
+        f = f1.add(f2);
+        assertEquals(Integer.MIN_VALUE + 1, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        f1 = Fraction.of(Integer.MAX_VALUE - 1, 1);
+        f2 = Fraction.ONE;
+        f = f1.add(f2);
+        assertEquals(Integer.MAX_VALUE, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        final Fraction overflower = f;
+        assertThrows(ArithmeticException.class, () -> overflower.add(Fraction.ONE)); // should overflow
+
+        // denominator should not be a multiple of 2 or 3 to trigger overflow
+        assertThrows(ArithmeticException.class, () -> Fraction.of(Integer.MIN_VALUE, 5).add(Fraction.of(-1, 5)));
+
+        final Fraction maxValue = Fraction.of(-Integer.MAX_VALUE, 1);
+        assertThrows(ArithmeticException.class, () -> maxValue.add(maxValue));
+
+        final Fraction negativeMaxValue = Fraction.of(-Integer.MAX_VALUE, 1);
+        assertThrows(ArithmeticException.class, () -> negativeMaxValue.add(negativeMaxValue));
+
+        final Fraction f3 = Fraction.of(3, 327680);
+        final Fraction f4 = Fraction.of(2, 59049);
+        assertThrows(ArithmeticException.class, () -> f3.add(f4)); // should overflow
+    }
+
+    @Test
+    public void testSubtract() {
+        Fraction f = null;
+        Fraction f1 = null;
+        Fraction f2 = null;
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(1, 5);
+        f = f1.subtract(f2);
+        assertEquals(2, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f1 = Fraction.of(7, 5);
+        f2 = Fraction.of(2, 5);
+        f = f1.subtract(f2);
+        assertEquals(1, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(3, 5);
+        f = f1.subtract(f2);
+        assertEquals(0, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(-4, 5);
+        f = f1.subtract(f2);
+        assertEquals(7, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f1 = Fraction.of(0, 5);
+        f2 = Fraction.of(4, 5);
+        f = f1.subtract(f2);
+        assertEquals(-4, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f1 = Fraction.of(0, 5);
+        f2 = Fraction.of(-4, 5);
+        f = f1.subtract(f2);
+        assertEquals(4, f.getNumerator());
+        assertEquals(5, f.getDenominator());
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(1, 2);
+        f = f1.subtract(f2);
+        assertEquals(1, f.getNumerator());
+        assertEquals(10, f.getDenominator());
+
+        f1 = Fraction.of(0, 5);
+        f2 = Fraction.of(1, 5);
+        f = f2.subtract(f1);
+        assertSame(f2, f);
+
+        final Fraction fr = f;
+        assertThrows(NullPointerException.class, () -> fr.subtract(null));
+
+        // if this fraction is subtracted naively, it will overflow.
+        // check that it doesn't.
+        f1 = Fraction.of(1, 32768 * 3);
+        f2 = Fraction.of(1, 59049);
+        f = f1.subtract(f2);
+        assertEquals(-13085, f.getNumerator());
+        assertEquals(1934917632, f.getDenominator());
+
+        f1 = Fraction.of(Integer.MIN_VALUE, 3);
+        f2 = Fraction.of(1, 3).negate();
+        f = f1.subtract(f2);
+        assertEquals(Integer.MIN_VALUE + 1, f.getNumerator());
+        assertEquals(3, f.getDenominator());
+
+        f1 = Fraction.of(Integer.MAX_VALUE, 1);
+        f2 = Fraction.ONE;
+        f = f1.subtract(f2);
+        assertEquals(Integer.MAX_VALUE - 1, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        // Should overflow
+        assertThrows(ArithmeticException.class, () -> Fraction.of(1, Integer.MAX_VALUE).subtract(Fraction.of(1, Integer.MAX_VALUE - 1)));
+        f = f1.subtract(f2);
+
+        // denominator should not be a multiple of 2 or 3 to trigger overflow
+        assertThrows(ArithmeticException.class, () -> Fraction.of(Integer.MIN_VALUE, 5).subtract(Fraction.of(1, 5)));
+
+        assertThrows(ArithmeticException.class, () -> Fraction.of(Integer.MIN_VALUE, 1).subtract(Fraction.ONE));
+
+        assertThrows(ArithmeticException.class, () -> Fraction.of(Integer.MAX_VALUE, 1).subtract(Fraction.ONE.negate()));
+
+        // Should overflow
+        assertThrows(ArithmeticException.class, () -> Fraction.of(3, 327680).subtract(Fraction.of(2, 59049)));
+    }
+
+    @Test
+    public void testMultiply() {
+        Fraction f = null;
+        Fraction f1 = null;
+        Fraction f2 = null;
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(2, 5);
+        f = f1.multiply(f2);
+        assertEquals(6, f.getNumerator());
+        assertEquals(25, f.getDenominator());
+
+        f1 = Fraction.of(6, 10);
+        f2 = Fraction.of(6, 10);
+        f = f1.multiply(f2);
+        assertEquals(9, f.getNumerator());
+        assertEquals(25, f.getDenominator());
+        f = f.multiply(f2);
+        assertEquals(27, f.getNumerator());
+        assertEquals(125, f.getDenominator());
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(-2, 5);
+        f = f1.multiply(f2);
+        assertEquals(-6, f.getNumerator());
+        assertEquals(25, f.getDenominator());
+
+        f1 = Fraction.of(-3, 5);
+        f2 = Fraction.of(-2, 5);
+        f = f1.multiply(f2);
+        assertEquals(6, f.getNumerator());
+        assertEquals(25, f.getDenominator());
+
+
+        f1 = Fraction.of(0, 5);
+        f2 = Fraction.of(2, 7);
+        f = f1.multiply(f2);
+        assertSame(Fraction.ZERO, f);
+
+        f1 = Fraction.of(2, 7);
+        f2 = Fraction.ONE;
+        f = f1.multiply(f2);
+        assertEquals(2, f.getNumerator());
+        assertEquals(7, f.getDenominator());
+
+        f1 = Fraction.of(Integer.MAX_VALUE, 1);
+        f2 = Fraction.of(Integer.MIN_VALUE, Integer.MAX_VALUE);
+        f = f1.multiply(f2);
+        assertEquals(Integer.MIN_VALUE, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        final Fraction fr = f;
+        assertThrows(NullPointerException.class, () -> fr.multiply(null));
+
+        final Fraction fr1 = Fraction.of(1, Integer.MAX_VALUE);
+        assertThrows(ArithmeticException.class, () -> fr1.multiply(fr1));
+
+        final Fraction fr2 = Fraction.of(1, -Integer.MAX_VALUE);
+        assertThrows(ArithmeticException.class, () -> fr2.multiply(fr2));
+    }
+
+    @Test
+    public void testDivide() {
+        Fraction f = null;
+        Fraction f1 = null;
+        Fraction f2 = null;
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(2, 5);
+        f = f1.divide(f2);
+        assertEquals(3, f.getNumerator());
+        assertEquals(2, f.getDenominator());
+
+        assertThrows(ArithmeticException.class, () -> Fraction.of(3, 5).divide(Fraction.ZERO));
+
+        f1 = Fraction.of(0, 5);
+        f2 = Fraction.of(2, 7);
+        f = f1.divide(f2);
+        assertSame(Fraction.ZERO, f);
+
+        f1 = Fraction.of(2, 7);
+        f2 = Fraction.ONE;
+        f = f1.divide(f2);
+        assertEquals(2, f.getNumerator());
+        assertEquals(7, f.getDenominator());
+
+        f1 = Fraction.of(1, Integer.MAX_VALUE);
+        f = f1.divide(f1);
+        assertEquals(1, f.getNumerator());
+        assertEquals(1, f.getDenominator());
+
+        f1 = Fraction.of(Integer.MIN_VALUE, Integer.MAX_VALUE);
+        f2 = Fraction.of(1, Integer.MAX_VALUE);
+        final Fraction fr = f1.divide(f2);
+        assertEquals(Integer.MIN_VALUE, fr.getNumerator());
+        assertEquals(1, fr.getDenominator());
+
+        assertThrows(NullPointerException.class, () -> fr.divide(null));
+
+        final Fraction smallest = Fraction.of(1, Integer.MAX_VALUE);
+        assertThrows(ArithmeticException.class, () -> smallest.divide(smallest.reciprocal())); // Should overflow
+
+        final Fraction negative = Fraction.of(1, -Integer.MAX_VALUE);
+        assertThrows(ArithmeticException.class, () -> negative.divide(negative.reciprocal())); // Should overflow
+    }
+
+    @Test
+    public void testEquals() {
+        Fraction f1 = null;
+        Fraction f2 = null;
+
+        f1 = Fraction.of(3, 5);
+        assertNotEquals(null, f1);
+        assertNotEquals(f1, new Object());
+        assertNotEquals(f1, Integer.valueOf(6));
+
+        f1 = Fraction.of(3, 5);
+        f2 = Fraction.of(2, 5);
+        assertNotEquals(f1, f2);
+        assertEquals(f1, f1);
+        assertEquals(f2, f2);
+
+        f2 = Fraction.of(3, 5);
+        assertEquals(f1, f2);
+
+        f2 = Fraction.of(6, 10);
+        assertEquals(f1, f2);
+    }
+
+    @Test
+    public void testHashCode() {
+        final Fraction f1 = Fraction.of(3, 5);
+        Fraction f2 = Fraction.of(3, 5);
+
+        assertEquals(f1.hashCode(), f2.hashCode());
+
+        f2 = Fraction.of(2, 5);
+        assertTrue(f1.hashCode() != f2.hashCode());
+
+        f2 = Fraction.of(6, 10);
+        assertEquals(f1.hashCode(), f2.hashCode());
+    }
+
+    @Test
+    public void testCompareTo() {
+        Fraction f1 = null;
+        Fraction f2 = null;
+
+        f1 = Fraction.of(3, 5);
+        assertEquals(0, f1.compareTo(f1));
+
+        final Fraction fr = f1;
+        assertThrows(NullPointerException.class, () -> fr.compareTo(null));
+
+        f2 = Fraction.of(2, 5);
+        assertTrue(f1.compareTo(f2) > 0);
+        assertEquals(0, f2.compareTo(f2));
+
+        f2 = Fraction.of(4, 5);
+        assertTrue(f1.compareTo(f2) < 0);
+        assertEquals(0, f2.compareTo(f2));
+
+        f2 = Fraction.of(3, 5);
+        assertEquals(0, f1.compareTo(f2));
+        assertEquals(0, f2.compareTo(f2));
+
+        f2 = Fraction.of(6, 10);
+        assertEquals(0, f1.compareTo(f2));
+        assertEquals(0, f2.compareTo(f2));
+
+        /*
+         *  Removed as not supported in numbers.
+         *
+         *  f2 = Fraction.of(-1, 1, Integer.MAX_VALUE);
+         *  assertTrue(f1.compareTo(f2) > 0);
+         *  assertEquals(0, f2.compareTo(f2));
+         */
+    }
+
+    @Test
+    public void testToString() {
+        Fraction f = null;
+
+        f = Fraction.of(3, 5);
+        final String str = f.toString();
+        assertEquals("3 / 5", str);
+        assertEquals(str, f.toString());
+
+        f = Fraction.of(7, 5);
+        assertEquals("7 / 5", f.toString());
+
+        f = Fraction.of(4, 2);
+        assertEquals("2", f.toString());
+
+        f = Fraction.of(0, 2);
+        assertEquals("0", f.toString());
+
+        f = Fraction.of(2, 2);
+        assertEquals("1", f.toString());
+
+        f = Fraction.of(Integer.MIN_VALUE);
+        assertEquals("-2147483648", f.toString());
+
+        f = Fraction.of(-1).add(Fraction.of(-1, Integer.MAX_VALUE));
+        assertEquals("-2147483648 / 2147483647", f.toString());
+    }
+
+/*
+ *  Removed as not supported in numbers.
+ *
+ *  @Test
+ *  public void testToProperString() {
+ *      Fraction f = null;
+ *
+ *      f = Fraction.of(3, 5);
+ *      final String str = f.toProperString();
+ *      assertEquals("3/5", str);
+ *      assertSame(str, f.toProperString());
+ *
+ *      f = Fraction.of(7, 5);
+ *      assertEquals("1 2/5", f.toProperString());
+ *
+ *      f = Fraction.of(14, 10);
+ *      assertEquals("1 2/5", f.toProperString());
+ *
+ *      f = Fraction.of(4, 2);
+ *      assertEquals("2", f.toProperString());
+ *
+ *      f = Fraction.of(0, 2);
+ *      assertEquals("0", f.toProperString());
+ *
+ *      f = Fraction.of(2, 2);
+ *      assertEquals("1", f.toProperString());
+ *
+ *      f = Fraction.of(-7, 5);
+ *      assertEquals("-1 2/5", f.toProperString());
+ *
+ *      f = Fraction.of(Integer.MIN_VALUE, 0, 1);
+ *      assertEquals("-2147483648", f.toProperString());
+ *
+ *      f = Fraction.of(-1, 1, Integer.MAX_VALUE);
+ *      assertEquals("-1 1/2147483647", f.toProperString());
+ *
+ *      assertEquals("-1", Fraction.of(-1).toProperString());
+ *  }
+ */
+}
diff --git a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java
index 1a2a1b6..8b5506d 100644
--- a/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java
+++ b/commons-numbers-fraction/src/test/java/org/apache/commons/numbers/fraction/FractionTest.java
@@ -622,6 +622,17 @@
         assertFraction(1, Integer.MAX_VALUE, b.divide(2));
     }
 
+    @Test
+    void testNumbers150() {
+        // zero to negative powers should throw an exception
+        Assertions.assertThrows(ArithmeticException.class, () -> Fraction.ZERO.pow(-1));
+        Assertions.assertThrows(ArithmeticException.class, () -> Fraction.ZERO.pow(Integer.MIN_VALUE));
+
+        // shall overflow
+        Assertions.assertThrows(ArithmeticException.class, () -> Fraction.of(2).pow(Integer.MIN_VALUE));
+        Assertions.assertThrows(ArithmeticException.class, () -> Fraction.of(1, 2).pow(Integer.MIN_VALUE));
+    }
+
     /**
      * Defines test cases that cause overflow in {@link Fraction#add(Fraction)}.
      * @return a list of test cases
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 07f1a6a..0f93d3b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -50,6 +50,37 @@
     <title>Apache Commons Numbers Release Notes</title>
   </properties>
   <body>
+    <release version="1.0-beta2" date="TBD" description="
+This is a beta release of Apache Commons Numbers. No guarantees are
+made regarding the stability of the API or compatibility with future
+releases.
+
+Apache Commons Numbers 1.0-beta1 contains the following library modules:
+  commons-numbers-angle (requires Java 8+)
+  commons-numbers-arrays (requires Java 8+)
+  commons-numbers-combinatorics (requires Java 8+)
+  commons-numbers-complex (requires Java 8+)
+  commons-numbers-core (requires Java 8+)
+  commons-numbers-field (requires Java 8+)
+  commons-numbers-fraction (requires Java 8+)
+  commons-numbers-gamma (requires Java 8+)
+  commons-numbers-primes (requires Java 8+)
+  commons-numbers-quaternion (requires Java 8+)
+  commons-numbers-rootfinder (requires Java 8+)
+">
+      <action dev="aherbert" type="fix" issue="NUMBERS-150" due-to="Jin Xu">
+        "Fraction/BigFraction": Fixed pow(int) to handle Integer.MIN_VALUE and throw
+        ArithmeticException for negative exponents to a fraction of zero.
+      </action>
+      <action dev="aherbert" type="update" issue="NUMBERS-151" due-to="Jin Xu">
+        "ArithmeticUtils": Refine pow(int, int) and pow(long, int) for edge cases.
+      </action>
+      <action dev="aherbert" type="update" issue="NUMBERS-149" due-to="Jin Xu">
+        "Fraction": Port tests from commons-lang Fraction to demonstrate functional compatibility
+        between the lang and numbers implementation of Fraction.
+      </action>
+    </release>
+
     <release version="1.0-beta1" date="2020-04-08" description="
 This is a beta release of Apache Commons Numbers. No guarantees are
 made regarding the stability of the API or compatibility with future