Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 75FB7200C02 for ; Fri, 20 Jan 2017 15:45:57 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 74B26160B48; Fri, 20 Jan 2017 14:45:57 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 6ED42160B5B for ; Fri, 20 Jan 2017 15:45:55 +0100 (CET) Received: (qmail 59298 invoked by uid 500); 20 Jan 2017 14:45:49 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 58163 invoked by uid 99); 20 Jan 2017 14:45:48 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 20 Jan 2017 14:45:48 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id C07C6F1755; Fri, 20 Jan 2017 14:45:48 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: erans@apache.org To: commits@commons.apache.org Date: Fri, 20 Jan 2017 14:45:57 -0000 Message-Id: <5c8f147b2a5d464390b676ad9c511caa@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [10/31] commons-numbers git commit: Multimodule project setup. archived-at: Fri, 20 Jan 2017 14:45:57 -0000 http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/PrecisionTest.java ---------------------------------------------------------------------- diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/PrecisionTest.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/PrecisionTest.java new file mode 100644 index 0000000..188c949 --- /dev/null +++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/PrecisionTest.java @@ -0,0 +1,547 @@ +/* + * 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 java.math.BigDecimal; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Test cases for the {@link Precision} class. + * + */ +public class PrecisionTest { + @Test + public void testEqualsWithRelativeTolerance() { + Assert.assertTrue(Precision.equalsWithRelativeTolerance(0d, 0d, 0d)); + Assert.assertTrue(Precision.equalsWithRelativeTolerance(0d, 1 / Double.NEGATIVE_INFINITY, 0d)); + + final double eps = 1e-14; + Assert.assertFalse(Precision.equalsWithRelativeTolerance(1.987654687654968, 1.987654687654988, eps)); + Assert.assertTrue(Precision.equalsWithRelativeTolerance(1.987654687654968, 1.987654687654987, eps)); + Assert.assertFalse(Precision.equalsWithRelativeTolerance(1.987654687654968, 1.987654687654948, eps)); + Assert.assertTrue(Precision.equalsWithRelativeTolerance(1.987654687654968, 1.987654687654949, eps)); + + Assert.assertFalse(Precision.equalsWithRelativeTolerance(Precision.SAFE_MIN, 0.0, eps)); + + Assert.assertFalse(Precision.equalsWithRelativeTolerance(1.0000000000001e-300, 1e-300, eps)); + Assert.assertTrue(Precision.equalsWithRelativeTolerance(1.00000000000001e-300, 1e-300, eps)); + + Assert.assertFalse(Precision.equalsWithRelativeTolerance(Double.NEGATIVE_INFINITY, 1.23, eps)); + Assert.assertFalse(Precision.equalsWithRelativeTolerance(Double.POSITIVE_INFINITY, 1.23, eps)); + + Assert.assertTrue(Precision.equalsWithRelativeTolerance(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, eps)); + Assert.assertTrue(Precision.equalsWithRelativeTolerance(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, eps)); + Assert.assertFalse(Precision.equalsWithRelativeTolerance(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, eps)); + + Assert.assertFalse(Precision.equalsWithRelativeTolerance(Double.NaN, 1.23, eps)); + Assert.assertFalse(Precision.equalsWithRelativeTolerance(Double.NaN, Double.NaN, eps)); + } + + @Test + public void testEqualsIncludingNaN() { + double[] testArray = { + Double.NaN, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + 1d, + 0d }; + for (int i = 0; i < testArray.length; i++) { + for (int j = 0; j < testArray.length; j++) { + if (i == j) { + Assert.assertTrue(Precision.equalsIncludingNaN(testArray[i], testArray[j])); + Assert.assertTrue(Precision.equalsIncludingNaN(testArray[j], testArray[i])); + } else { + Assert.assertTrue(!Precision.equalsIncludingNaN(testArray[i], testArray[j])); + Assert.assertTrue(!Precision.equalsIncludingNaN(testArray[j], testArray[i])); + } + } + } + } + + @Test + public void testEqualsWithAllowedDelta() { + Assert.assertTrue(Precision.equals(153.0000, 153.0000, .0625)); + Assert.assertTrue(Precision.equals(153.0000, 153.0625, .0625)); + Assert.assertTrue(Precision.equals(152.9375, 153.0000, .0625)); + Assert.assertFalse(Precision.equals(153.0000, 153.0625, .0624)); + Assert.assertFalse(Precision.equals(152.9374, 153.0000, .0625)); + Assert.assertFalse(Precision.equals(Double.NaN, Double.NaN, 1.0)); + Assert.assertTrue(Precision.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0)); + Assert.assertTrue(Precision.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1.0)); + Assert.assertFalse(Precision.equals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0)); + } + + @Test + public void testMath475() { + final double a = 1.7976931348623182E16; + final double b = Math.nextUp(a); + + double diff = Math.abs(a - b); + // Because they are adjacent floating point numbers, "a" and "b" are + // considered equal even though the allowed error is smaller than + // their difference. + Assert.assertTrue(Precision.equals(a, b, 0.5 * diff)); + + final double c = Math.nextUp(b); + diff = Math.abs(a - c); + // Because "a" and "c" are not adjacent, the tolerance is taken into + // account for assessing equality. + Assert.assertTrue(Precision.equals(a, c, diff)); + Assert.assertFalse(Precision.equals(a, c, (1 - 1e-16) * diff)); + } + + @Test + public void testEqualsIncludingNaNWithAllowedDelta() { + Assert.assertTrue(Precision.equalsIncludingNaN(153.0000, 153.0000, .0625)); + Assert.assertTrue(Precision.equalsIncludingNaN(153.0000, 153.0625, .0625)); + Assert.assertTrue(Precision.equalsIncludingNaN(152.9375, 153.0000, .0625)); + Assert.assertTrue(Precision.equalsIncludingNaN(Double.NaN, Double.NaN, 1.0)); + Assert.assertTrue(Precision.equalsIncludingNaN(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0)); + Assert.assertTrue(Precision.equalsIncludingNaN(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1.0)); + Assert.assertFalse(Precision.equalsIncludingNaN(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 1.0)); + Assert.assertFalse(Precision.equalsIncludingNaN(153.0000, 153.0625, .0624)); + Assert.assertFalse(Precision.equalsIncludingNaN(152.9374, 153.0000, .0625)); + } + + // Tests for floating point equality + @Test + public void testFloatEqualsWithAllowedUlps() { + Assert.assertTrue("+0.0f == -0.0f",Precision.equals(0.0f, -0.0f)); + Assert.assertTrue("+0.0f == -0.0f (1 ulp)",Precision.equals(0.0f, -0.0f, 1)); + float oneFloat = 1.0f; + Assert.assertTrue("1.0f == 1.0f + 1 ulp",Precision.equals(oneFloat, Float.intBitsToFloat(1 + Float.floatToIntBits(oneFloat)))); + Assert.assertTrue("1.0f == 1.0f + 1 ulp (1 ulp)",Precision.equals(oneFloat, Float.intBitsToFloat(1 + Float.floatToIntBits(oneFloat)), 1)); + Assert.assertFalse("1.0f != 1.0f + 2 ulp (1 ulp)",Precision.equals(oneFloat, Float.intBitsToFloat(2 + Float.floatToIntBits(oneFloat)), 1)); + + Assert.assertTrue(Precision.equals(153.0f, 153.0f, 1)); + + // These tests need adjusting for floating point precision +// Assert.assertTrue(Precision.equals(153.0f, 153.00000000000003f, 1)); +// Assert.assertFalse(Precision.equals(153.0f, 153.00000000000006f, 1)); +// Assert.assertTrue(Precision.equals(153.0f, 152.99999999999997f, 1)); +// Assert.assertFalse(Precision.equals(153f, 152.99999999999994f, 1)); +// +// Assert.assertTrue(Precision.equals(-128.0f, -127.99999999999999f, 1)); +// Assert.assertFalse(Precision.equals(-128.0f, -127.99999999999997f, 1)); +// Assert.assertTrue(Precision.equals(-128.0f, -128.00000000000003f, 1)); +// Assert.assertFalse(Precision.equals(-128.0f, -128.00000000000006f, 1)); + + Assert.assertTrue(Precision.equals(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, 1)); + Assert.assertTrue(Precision.equals(Double.MAX_VALUE, Float.POSITIVE_INFINITY, 1)); + + Assert.assertTrue(Precision.equals(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, 1)); + Assert.assertTrue(Precision.equals(-Float.MAX_VALUE, Float.NEGATIVE_INFINITY, 1)); + + Assert.assertFalse(Precision.equals(Float.NaN, Float.NaN, 1)); + Assert.assertFalse(Precision.equals(Float.NaN, Float.NaN, 0)); + Assert.assertFalse(Precision.equals(Float.NaN, 0, 0)); + Assert.assertFalse(Precision.equals(Float.NaN, Float.POSITIVE_INFINITY, 0)); + Assert.assertFalse(Precision.equals(Float.NaN, Float.NEGATIVE_INFINITY, 0)); + + Assert.assertFalse(Precision.equals(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, 100000)); + } + + @Test + public void testEqualsWithAllowedUlps() { + Assert.assertTrue(Precision.equals(0.0, -0.0, 1)); + + Assert.assertTrue(Precision.equals(1.0, 1 + Math.ulp(1d), 1)); + Assert.assertFalse(Precision.equals(1.0, 1 + 2 * Math.ulp(1d), 1)); + + final double nUp1 = Math.nextAfter(1d, Double.POSITIVE_INFINITY); + final double nnUp1 = Math.nextAfter(nUp1, Double.POSITIVE_INFINITY); + Assert.assertTrue(Precision.equals(1.0, nUp1, 1)); + Assert.assertTrue(Precision.equals(nUp1, nnUp1, 1)); + Assert.assertFalse(Precision.equals(1.0, nnUp1, 1)); + + Assert.assertTrue(Precision.equals(0.0, Math.ulp(0d), 1)); + Assert.assertTrue(Precision.equals(0.0, -Math.ulp(0d), 1)); + + Assert.assertTrue(Precision.equals(153.0, 153.0, 1)); + + Assert.assertTrue(Precision.equals(153.0, 153.00000000000003, 1)); + Assert.assertFalse(Precision.equals(153.0, 153.00000000000006, 1)); + Assert.assertTrue(Precision.equals(153.0, 152.99999999999997, 1)); + Assert.assertFalse(Precision.equals(153, 152.99999999999994, 1)); + + Assert.assertTrue(Precision.equals(-128.0, -127.99999999999999, 1)); + Assert.assertFalse(Precision.equals(-128.0, -127.99999999999997, 1)); + Assert.assertTrue(Precision.equals(-128.0, -128.00000000000003, 1)); + Assert.assertFalse(Precision.equals(-128.0, -128.00000000000006, 1)); + + Assert.assertTrue(Precision.equals(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1)); + Assert.assertTrue(Precision.equals(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 1)); + + Assert.assertTrue(Precision.equals(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1)); + Assert.assertTrue(Precision.equals(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY, 1)); + + Assert.assertFalse(Precision.equals(Double.NaN, Double.NaN, 1)); + Assert.assertFalse(Precision.equals(Double.NaN, Double.NaN, 0)); + Assert.assertFalse(Precision.equals(Double.NaN, 0, 0)); + Assert.assertFalse(Precision.equals(Double.NaN, Double.POSITIVE_INFINITY, 0)); + Assert.assertFalse(Precision.equals(Double.NaN, Double.NEGATIVE_INFINITY, 0)); + + Assert.assertFalse(Precision.equals(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 100000)); + } + + @Test + public void testEqualsIncludingNaNWithAllowedUlps() { + Assert.assertTrue(Precision.equalsIncludingNaN(0.0, -0.0, 1)); + + Assert.assertTrue(Precision.equalsIncludingNaN(1.0, 1 + Math.ulp(1d), 1)); + Assert.assertFalse(Precision.equalsIncludingNaN(1.0, 1 + 2 * Math.ulp(1d), 1)); + + final double nUp1 = Math.nextAfter(1d, Double.POSITIVE_INFINITY); + final double nnUp1 = Math.nextAfter(nUp1, Double.POSITIVE_INFINITY); + Assert.assertTrue(Precision.equalsIncludingNaN(1.0, nUp1, 1)); + Assert.assertTrue(Precision.equalsIncludingNaN(nUp1, nnUp1, 1)); + Assert.assertFalse(Precision.equalsIncludingNaN(1.0, nnUp1, 1)); + + Assert.assertTrue(Precision.equalsIncludingNaN(0.0, Math.ulp(0d), 1)); + Assert.assertTrue(Precision.equalsIncludingNaN(0.0, -Math.ulp(0d), 1)); + + Assert.assertTrue(Precision.equalsIncludingNaN(153.0, 153.0, 1)); + + Assert.assertTrue(Precision.equalsIncludingNaN(153.0, 153.00000000000003, 1)); + Assert.assertFalse(Precision.equalsIncludingNaN(153.0, 153.00000000000006, 1)); + Assert.assertTrue(Precision.equalsIncludingNaN(153.0, 152.99999999999997, 1)); + Assert.assertFalse(Precision.equalsIncludingNaN(153, 152.99999999999994, 1)); + + Assert.assertTrue(Precision.equalsIncludingNaN(-128.0, -127.99999999999999, 1)); + Assert.assertFalse(Precision.equalsIncludingNaN(-128.0, -127.99999999999997, 1)); + Assert.assertTrue(Precision.equalsIncludingNaN(-128.0, -128.00000000000003, 1)); + Assert.assertFalse(Precision.equalsIncludingNaN(-128.0, -128.00000000000006, 1)); + + Assert.assertTrue(Precision.equalsIncludingNaN(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1)); + Assert.assertTrue(Precision.equalsIncludingNaN(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 1)); + + Assert.assertTrue(Precision.equalsIncludingNaN(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, 1)); + Assert.assertTrue(Precision.equalsIncludingNaN(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY, 1)); + + Assert.assertTrue(Precision.equalsIncludingNaN(Double.NaN, Double.NaN, 1)); + + Assert.assertFalse(Precision.equalsIncludingNaN(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 100000)); + } + + @Test + public void testCompareToEpsilon() { + Assert.assertEquals(0, Precision.compareTo(152.33, 152.32, .011)); + Assert.assertTrue(Precision.compareTo(152.308, 152.32, .011) < 0); + Assert.assertTrue(Precision.compareTo(152.33, 152.318, .011) > 0); + Assert.assertEquals(0, Precision.compareTo(Double.MIN_VALUE, +0.0, Double.MIN_VALUE)); + Assert.assertEquals(0, Precision.compareTo(Double.MIN_VALUE, -0.0, Double.MIN_VALUE)); + } + + @Test + public void testCompareToMaxUlps() { + double a = 152.32; + double delta = Math.ulp(a); + for (int i = 0; i <= 10; ++i) { + if (i <= 5) { + Assert.assertEquals( 0, Precision.compareTo(a, a + i * delta, 5)); + Assert.assertEquals( 0, Precision.compareTo(a, a - i * delta, 5)); + } else { + Assert.assertEquals(-1, Precision.compareTo(a, a + i * delta, 5)); + Assert.assertEquals(+1, Precision.compareTo(a, a - i * delta, 5)); + } + } + + Assert.assertEquals( 0, Precision.compareTo(-0.0, 0.0, 0)); + + Assert.assertEquals(-1, Precision.compareTo(-Double.MIN_VALUE, -0.0, 0)); + Assert.assertEquals( 0, Precision.compareTo(-Double.MIN_VALUE, -0.0, 1)); + Assert.assertEquals(-1, Precision.compareTo(-Double.MIN_VALUE, +0.0, 0)); + Assert.assertEquals( 0, Precision.compareTo(-Double.MIN_VALUE, +0.0, 1)); + + Assert.assertEquals(+1, Precision.compareTo( Double.MIN_VALUE, -0.0, 0)); + Assert.assertEquals( 0, Precision.compareTo( Double.MIN_VALUE, -0.0, 1)); + Assert.assertEquals(+1, Precision.compareTo( Double.MIN_VALUE, +0.0, 0)); + Assert.assertEquals( 0, Precision.compareTo( Double.MIN_VALUE, +0.0, 1)); + + Assert.assertEquals(-1, Precision.compareTo(-Double.MIN_VALUE, Double.MIN_VALUE, 0)); + Assert.assertEquals(-1, Precision.compareTo(-Double.MIN_VALUE, Double.MIN_VALUE, 1)); + Assert.assertEquals( 0, Precision.compareTo(-Double.MIN_VALUE, Double.MIN_VALUE, 2)); + + Assert.assertEquals( 0, Precision.compareTo(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 1)); + Assert.assertEquals(-1, Precision.compareTo(Double.MAX_VALUE, Double.POSITIVE_INFINITY, 0)); + + Assert.assertEquals(+1, Precision.compareTo(Double.MAX_VALUE, Double.NaN, Integer.MAX_VALUE)); + Assert.assertEquals(+1, Precision.compareTo(Double.NaN, Double.MAX_VALUE, Integer.MAX_VALUE)); + } + + @Test + public void testRoundDouble() { + double x = 1.234567890; + Assert.assertEquals(1.23, Precision.round(x, 2), 0.0); + Assert.assertEquals(1.235, Precision.round(x, 3), 0.0); + Assert.assertEquals(1.2346, Precision.round(x, 4), 0.0); + + // JIRA MATH-151 + Assert.assertEquals(39.25, Precision.round(39.245, 2), 0.0); + Assert.assertEquals(39.24, Precision.round(39.245, 2, BigDecimal.ROUND_DOWN), 0.0); + double xx = 39.0; + xx += 245d / 1000d; + Assert.assertEquals(39.25, Precision.round(xx, 2), 0.0); + + // BZ 35904 + Assert.assertEquals(30.1d, Precision.round(30.095d, 2), 0.0d); + Assert.assertEquals(30.1d, Precision.round(30.095d, 1), 0.0d); + Assert.assertEquals(33.1d, Precision.round(33.095d, 1), 0.0d); + Assert.assertEquals(33.1d, Precision.round(33.095d, 2), 0.0d); + Assert.assertEquals(50.09d, Precision.round(50.085d, 2), 0.0d); + Assert.assertEquals(50.19d, Precision.round(50.185d, 2), 0.0d); + Assert.assertEquals(50.01d, Precision.round(50.005d, 2), 0.0d); + Assert.assertEquals(30.01d, Precision.round(30.005d, 2), 0.0d); + Assert.assertEquals(30.65d, Precision.round(30.645d, 2), 0.0d); + + Assert.assertEquals(1.24, Precision.round(x, 2, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(1.235, Precision.round(x, 3, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(1.2346, Precision.round(x, 4, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(-1.23, Precision.round(-x, 2, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(-1.234, Precision.round(-x, 3, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(-1.2345, Precision.round(-x, 4, BigDecimal.ROUND_CEILING), 0.0); + + Assert.assertEquals(1.23, Precision.round(x, 2, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(1.234, Precision.round(x, 3, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(1.2345, Precision.round(x, 4, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(-1.23, Precision.round(-x, 2, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(-1.234, Precision.round(-x, 3, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(-1.2345, Precision.round(-x, 4, BigDecimal.ROUND_DOWN), 0.0); + + Assert.assertEquals(1.23, Precision.round(x, 2, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(1.234, Precision.round(x, 3, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(1.2345, Precision.round(x, 4, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(-1.24, Precision.round(-x, 2, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(-1.235, Precision.round(-x, 3, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(-1.2346, Precision.round(-x, 4, BigDecimal.ROUND_FLOOR), 0.0); + + Assert.assertEquals(1.23, Precision.round(x, 2, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(1.235, Precision.round(x, 3, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(1.2346, Precision.round(x, 4, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(-1.23, Precision.round(-x, 2, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(-1.235, Precision.round(-x, 3, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(-1.2346, Precision.round(-x, 4, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(1.234, Precision.round(1.2345, 3, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(-1.234, Precision.round(-1.2345, 3, BigDecimal.ROUND_HALF_DOWN), 0.0); + + Assert.assertEquals(1.23, Precision.round(x, 2, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(1.235, Precision.round(x, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(1.2346, Precision.round(x, 4, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.23, Precision.round(-x, 2, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.235, Precision.round(-x, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.2346, Precision.round(-x, 4, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(1.234, Precision.round(1.2345, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.234, Precision.round(-1.2345, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(1.236, Precision.round(1.2355, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.236, Precision.round(-1.2355, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + + Assert.assertEquals(1.23, Precision.round(x, 2, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(1.235, Precision.round(x, 3, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(1.2346, Precision.round(x, 4, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(-1.23, Precision.round(-x, 2, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(-1.235, Precision.round(-x, 3, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(-1.2346, Precision.round(-x, 4, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(1.235, Precision.round(1.2345, 3, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(-1.235, Precision.round(-1.2345, 3, BigDecimal.ROUND_HALF_UP), 0.0); + + Assert.assertEquals(-1.23, Precision.round(-1.23, 2, BigDecimal.ROUND_UNNECESSARY), 0.0); + Assert.assertEquals(1.23, Precision.round(1.23, 2, BigDecimal.ROUND_UNNECESSARY), 0.0); + + try { + Precision.round(1.234, 2, BigDecimal.ROUND_UNNECESSARY); + Assert.fail(); + } catch (ArithmeticException ex) { + // expected + } + + Assert.assertEquals(1.24, Precision.round(x, 2, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(1.235, Precision.round(x, 3, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(1.2346, Precision.round(x, 4, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(-1.24, Precision.round(-x, 2, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(-1.235, Precision.round(-x, 3, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(-1.2346, Precision.round(-x, 4, BigDecimal.ROUND_UP), 0.0); + + try { + Precision.round(1.234, 2, 1923); + Assert.fail(); + } catch (IllegalArgumentException ex) { + // expected + } + + // MATH-151 + Assert.assertEquals(39.25, Precision.round(39.245, 2, BigDecimal.ROUND_HALF_UP), 0.0); + + // special values + TestUtils.assertEquals(Double.NaN, Precision.round(Double.NaN, 2), 0.0); + Assert.assertEquals(0.0, Precision.round(0.0, 2), 0.0); + Assert.assertEquals(Double.POSITIVE_INFINITY, Precision.round(Double.POSITIVE_INFINITY, 2), 0.0); + Assert.assertEquals(Double.NEGATIVE_INFINITY, Precision.round(Double.NEGATIVE_INFINITY, 2), 0.0); + // comparison of positive and negative zero is not possible -> always equal thus do string comparison + Assert.assertEquals("-0.0", Double.toString(Precision.round(-0.0, 0))); + Assert.assertEquals("-0.0", Double.toString(Precision.round(-1e-10, 0))); + } + + @Test + public void testRoundFloat() { + float x = 1.234567890f; + Assert.assertEquals(1.23f, Precision.round(x, 2), 0.0); + Assert.assertEquals(1.235f, Precision.round(x, 3), 0.0); + Assert.assertEquals(1.2346f, Precision.round(x, 4), 0.0); + + // BZ 35904 + Assert.assertEquals(30.1f, Precision.round(30.095f, 2), 0.0f); + Assert.assertEquals(30.1f, Precision.round(30.095f, 1), 0.0f); + Assert.assertEquals(50.09f, Precision.round(50.085f, 2), 0.0f); + Assert.assertEquals(50.19f, Precision.round(50.185f, 2), 0.0f); + Assert.assertEquals(50.01f, Precision.round(50.005f, 2), 0.0f); + Assert.assertEquals(30.01f, Precision.round(30.005f, 2), 0.0f); + Assert.assertEquals(30.65f, Precision.round(30.645f, 2), 0.0f); + + Assert.assertEquals(1.24f, Precision.round(x, 2, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(1.235f, Precision.round(x, 3, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(1.2346f, Precision.round(x, 4, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(-1.23f, Precision.round(-x, 2, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(-1.234f, Precision.round(-x, 3, BigDecimal.ROUND_CEILING), 0.0); + Assert.assertEquals(-1.2345f, Precision.round(-x, 4, BigDecimal.ROUND_CEILING), 0.0); + + Assert.assertEquals(1.23f, Precision.round(x, 2, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(1.234f, Precision.round(x, 3, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(1.2345f, Precision.round(x, 4, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(-1.23f, Precision.round(-x, 2, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(-1.234f, Precision.round(-x, 3, BigDecimal.ROUND_DOWN), 0.0); + Assert.assertEquals(-1.2345f, Precision.round(-x, 4, BigDecimal.ROUND_DOWN), 0.0); + + Assert.assertEquals(1.23f, Precision.round(x, 2, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(1.234f, Precision.round(x, 3, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(1.2345f, Precision.round(x, 4, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(-1.24f, Precision.round(-x, 2, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(-1.235f, Precision.round(-x, 3, BigDecimal.ROUND_FLOOR), 0.0); + Assert.assertEquals(-1.2346f, Precision.round(-x, 4, BigDecimal.ROUND_FLOOR), 0.0); + + Assert.assertEquals(1.23f, Precision.round(x, 2, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(1.235f, Precision.round(x, 3, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(1.2346f, Precision.round(x, 4, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(-1.23f, Precision.round(-x, 2, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(-1.235f, Precision.round(-x, 3, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(-1.2346f, Precision.round(-x, 4, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(1.234f, Precision.round(1.2345f, 3, BigDecimal.ROUND_HALF_DOWN), 0.0); + Assert.assertEquals(-1.234f, Precision.round(-1.2345f, 3, BigDecimal.ROUND_HALF_DOWN), 0.0); + + Assert.assertEquals(1.23f, Precision.round(x, 2, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(1.235f, Precision.round(x, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(1.2346f, Precision.round(x, 4, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.23f, Precision.round(-x, 2, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.235f, Precision.round(-x, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.2346f, Precision.round(-x, 4, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(1.234f, Precision.round(1.2345f, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.234f, Precision.round(-1.2345f, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(1.236f, Precision.round(1.2355f, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + Assert.assertEquals(-1.236f, Precision.round(-1.2355f, 3, BigDecimal.ROUND_HALF_EVEN), 0.0); + + Assert.assertEquals(1.23f, Precision.round(x, 2, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(1.235f, Precision.round(x, 3, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(1.2346f, Precision.round(x, 4, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(-1.23f, Precision.round(-x, 2, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(-1.235f, Precision.round(-x, 3, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(-1.2346f, Precision.round(-x, 4, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(1.235f, Precision.round(1.2345f, 3, BigDecimal.ROUND_HALF_UP), 0.0); + Assert.assertEquals(-1.235f, Precision.round(-1.2345f, 3, BigDecimal.ROUND_HALF_UP), 0.0); + + Assert.assertEquals(-1.23f, Precision.round(-1.23f, 2, BigDecimal.ROUND_UNNECESSARY), 0.0); + Assert.assertEquals(1.23f, Precision.round(1.23f, 2, BigDecimal.ROUND_UNNECESSARY), 0.0); + + try { + Precision.round(1.234f, 2, BigDecimal.ROUND_UNNECESSARY); + Assert.fail(); + } catch (ArithmeticException ex) { + // success + } + + Assert.assertEquals(1.24f, Precision.round(x, 2, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(1.235f, Precision.round(x, 3, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(1.2346f, Precision.round(x, 4, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(-1.24f, Precision.round(-x, 2, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(-1.235f, Precision.round(-x, 3, BigDecimal.ROUND_UP), 0.0); + Assert.assertEquals(-1.2346f, Precision.round(-x, 4, BigDecimal.ROUND_UP), 0.0); + + try { + Precision.round(1.234f, 2, 1923); + Assert.fail(); + } catch (IllegalArgumentException ex) { + // success + } + + // special values + TestUtils.assertEquals(Float.NaN, Precision.round(Float.NaN, 2), 0.0f); + Assert.assertEquals(0.0f, Precision.round(0.0f, 2), 0.0f); + Assert.assertEquals(Float.POSITIVE_INFINITY, Precision.round(Float.POSITIVE_INFINITY, 2), 0.0f); + Assert.assertEquals(Float.NEGATIVE_INFINITY, Precision.round(Float.NEGATIVE_INFINITY, 2), 0.0f); + // comparison of positive and negative zero is not possible -> always equal thus do string comparison + Assert.assertEquals("-0.0", Float.toString(Precision.round(-0.0f, 0))); + Assert.assertEquals("-0.0", Float.toString(Precision.round(-1e-10f, 0))); + + // MATH-1070 + Assert.assertEquals(0.0f, Precision.round(0f, 2, BigDecimal.ROUND_UP), 0.0f); + Assert.assertEquals(0.05f, Precision.round(0.05f, 2, BigDecimal.ROUND_UP), 0.0f); + Assert.assertEquals(0.06f, Precision.round(0.051f, 2, BigDecimal.ROUND_UP), 0.0f); + Assert.assertEquals(0.06f, Precision.round(0.0505f, 2, BigDecimal.ROUND_UP), 0.0f); + Assert.assertEquals(0.06f, Precision.round(0.059f, 2, BigDecimal.ROUND_UP), 0.0f); + } + + + @Test + public void testIssue721() { + Assert.assertEquals(-53, Math.getExponent(Precision.EPSILON)); + Assert.assertEquals(-1022, Math.getExponent(Precision.SAFE_MIN)); + } + + + @Test + public void testRepresentableDelta() { + int nonRepresentableCount = 0; + final double x = 100; + final int numTrials = 10000; + for (int i = 0; i < numTrials; i++) { + final double originalDelta = Math.random(); + final double delta = Precision.representableDelta(x, originalDelta); + if (delta != originalDelta) { + ++nonRepresentableCount; + } + } + + Assert.assertTrue(nonRepresentableCount / (double) numTrials > 0.9); + } + + @Test + public void testMath843() { + final double afterEpsilon = Math.nextAfter(Precision.EPSILON, + Double.POSITIVE_INFINITY); + + // a) 1 + EPSILON is equal to 1. + Assert.assertTrue(1 + Precision.EPSILON == 1); + + // b) 1 + "the number after EPSILON" is not equal to 1. + Assert.assertFalse(1 + afterEpsilon == 1); + } + + @Test + public void testMath1127() { + Assert.assertFalse(Precision.equals(2.0, -2.0, 1)); + Assert.assertTrue(Precision.equals(0.0, -0.0, 0)); + Assert.assertFalse(Precision.equals(2.0f, -2.0f, 1)); + Assert.assertTrue(Precision.equals(0.0f, -0.0f, 0)); + } +} http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/TestUtils.java ---------------------------------------------------------------------- diff --git a/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/TestUtils.java b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/TestUtils.java new file mode 100644 index 0000000..6700958 --- /dev/null +++ b/commons-numbers-core/src/test/java/org/apache/commons/numbers/core/TestUtils.java @@ -0,0 +1,320 @@ +/* + * 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 java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + + +import org.junit.Assert; + +/** + * Test utilities. + * TODO: Cleanup (remove unused and obsolete methods). + */ +public class TestUtils { + /** + * Collection of static methods used in math unit tests. + */ + private TestUtils() { + super(); + } + + /** + * Verifies that expected and actual are within delta, or are both NaN or + * infinities of the same sign. + */ + public static void assertEquals(double expected, double actual, double delta) { + Assert.assertEquals(null, expected, actual, delta); + } + + /** + * Verifies that expected and actual are within delta, or are both NaN or + * infinities of the same sign. + */ + public static void assertEquals(String msg, double expected, double actual, double delta) { + // check for NaN + if(Double.isNaN(expected)){ + Assert.assertTrue("" + actual + " is not NaN.", + Double.isNaN(actual)); + } else { + Assert.assertEquals(msg, expected, actual, delta); + } + } + + /** + * Verifies that the two arguments are exactly the same, either + * both NaN or infinities of same sign, or identical floating point values. + */ + public static void assertSame(double expected, double actual) { + Assert.assertEquals(expected, actual, 0); + } + + /** + * Verifies that two double arrays have equal entries, up to tolerance + */ + public static void assertEquals(double expected[], double observed[], double tolerance) { + assertEquals("Array comparison failure", expected, observed, tolerance); + } + + /** + * Serializes an object to a bytes array and then recovers the object from the bytes array. + * Returns the deserialized object. + * + * @param o object to serialize and recover + * @return the recovered, deserialized object + */ + public static Object serializeAndRecover(Object o) { + try { + // serialize the Object + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream so = new ObjectOutputStream(bos); + so.writeObject(o); + + // deserialize the Object + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + ObjectInputStream si = new ObjectInputStream(bis); + return si.readObject(); + } catch (IOException ioe) { + return null; + } catch (ClassNotFoundException cnfe) { + return null; + } + } + + /** + * Verifies that serialization preserves equals and hashCode. + * Serializes the object, then recovers it and checks equals and hash code. + * + * @param object the object to serialize and recover + */ + public static void checkSerializedEquality(Object object) { + Object object2 = serializeAndRecover(object); + Assert.assertEquals("Equals check", object, object2); + Assert.assertEquals("HashCode check", object.hashCode(), object2.hashCode()); + } + + /** + * Verifies that the relative error in actual vs. expected is less than or + * equal to relativeError. If expected is infinite or NaN, actual must be + * the same (NaN or infinity of the same sign). + * + * @param expected expected value + * @param actual observed value + * @param relativeError maximum allowable relative error + */ + public static void assertRelativelyEquals(double expected, double actual, + double relativeError) { + assertRelativelyEquals(null, expected, actual, relativeError); + } + + /** + * Verifies that the relative error in actual vs. expected is less than or + * equal to relativeError. If expected is infinite or NaN, actual must be + * the same (NaN or infinity of the same sign). + * + * @param msg message to return with failure + * @param expected expected value + * @param actual observed value + * @param relativeError maximum allowable relative error + */ + public static void assertRelativelyEquals(String msg, double expected, + double actual, double relativeError) { + if (Double.isNaN(expected)) { + Assert.assertTrue(msg, Double.isNaN(actual)); + } else if (Double.isNaN(actual)) { + Assert.assertTrue(msg, Double.isNaN(expected)); + } else if (Double.isInfinite(actual) || Double.isInfinite(expected)) { + Assert.assertEquals(expected, actual, relativeError); + } else if (expected == 0.0) { + Assert.assertEquals(msg, actual, expected, relativeError); + } else { + double absError = Math.abs(expected) * relativeError; + Assert.assertEquals(msg, expected, actual, absError); + } + } + + /** + * Fails iff values does not contain a number within epsilon of x. + * + * @param msg message to return with failure + * @param values double array to search + * @param x value sought + * @param epsilon tolerance + */ + public static void assertContains(String msg, double[] values, + double x, double epsilon) { + for (double value : values) { + if (Precision.equals(value, x, epsilon)) { + return; + } + } + Assert.fail(msg + " Unable to find " + x); + } + + /** + * Fails iff values does not contain a number within epsilon of x. + * + * @param values double array to search + * @param x value sought + * @param epsilon tolerance + */ + public static void assertContains(double[] values, double x, + double epsilon) { + assertContains(null, values, x, epsilon); + } + + /** verifies that two arrays are close (sup norm) */ + public static void assertEquals(String msg, double[] expected, double[] observed, double tolerance) { + StringBuilder out = new StringBuilder(msg); + if (expected.length != observed.length) { + out.append("\n Arrays not same length. \n"); + out.append("expected has length "); + out.append(expected.length); + out.append(" observed length = "); + out.append(observed.length); + Assert.fail(out.toString()); + } + boolean failure = false; + for (int i=0; i < expected.length; i++) { + if (!equalsIncludingNaN(expected[i], observed[i], tolerance)) { + failure = true; + out.append("\n Elements at index "); + out.append(i); + out.append(" differ. "); + out.append(" expected = "); + out.append(expected[i]); + out.append(" observed = "); + out.append(observed[i]); + } + } + if (failure) { + Assert.fail(out.toString()); + } + } + + /** verifies that two arrays are close (sup norm) */ + public static void assertEquals(String msg, float[] expected, float[] observed, float tolerance) { + StringBuilder out = new StringBuilder(msg); + if (expected.length != observed.length) { + out.append("\n Arrays not same length. \n"); + out.append("expected has length "); + out.append(expected.length); + out.append(" observed length = "); + out.append(observed.length); + Assert.fail(out.toString()); + } + boolean failure = false; + for (int i=0; i < expected.length; i++) { + if (!equalsIncludingNaN(expected[i], observed[i], tolerance)) { + failure = true; + out.append("\n Elements at index "); + out.append(i); + out.append(" differ. "); + out.append(" expected = "); + out.append(expected[i]); + out.append(" observed = "); + out.append(observed[i]); + } + } + if (failure) { + Assert.fail(out.toString()); + } + } + + /** + * Updates observed counts of values in quartiles. + * counts[0] <-> 1st quartile ... counts[3] <-> top quartile + */ + public static void updateCounts(double value, long[] counts, double[] quartiles) { + if (value < quartiles[0]) { + counts[0]++; + } else if (value > quartiles[2]) { + counts[3]++; + } else if (value > quartiles[1]) { + counts[2]++; + } else { + counts[1]++; + } + } + + /** + * Eliminates points with zero mass from densityPoints and densityValues parallel + * arrays. Returns the number of positive mass points and collapses the arrays so + * that the first elements of the input arrays represent the positive + * mass points. + */ + public static int eliminateZeroMassPoints(int[] densityPoints, double[] densityValues) { + int positiveMassCount = 0; + for (int i = 0; i < densityValues.length; i++) { + if (densityValues[i] > 0) { + positiveMassCount++; + } + } + if (positiveMassCount < densityValues.length) { + int[] newPoints = new int[positiveMassCount]; + double[] newValues = new double[positiveMassCount]; + int j = 0; + for (int i = 0; i < densityValues.length; i++) { + if (densityValues[i] > 0) { + newPoints[j] = densityPoints[i]; + newValues[j] = densityValues[i]; + j++; + } + } + System.arraycopy(newPoints,0,densityPoints,0,positiveMassCount); + System.arraycopy(newValues,0,densityValues,0,positiveMassCount); + } + return positiveMassCount; + } + + /** + * Returns true if the arguments are both NaN, are equal or are within the range + * of allowed error (inclusive). + * + * @param x first value + * @param y second value + * @param eps the amount of absolute error to allow. + * @return {@code true} if the values are equal or within range of each other, + * or both are NaN. + * @since 2.2 + */ + private static boolean equalsIncludingNaN(double x, double y, double eps) { + return equalsIncludingNaN(x, y) || (Math.abs(y - x) <= eps); + } + + /** + * Returns true if the arguments are both NaN or they are + * equal as defined by {@link #equals(double,double) equals(x, y, 1)}. + * + * @param x first value + * @param y second value + * @return {@code true} if the values are equal or both are NaN. + * @since 2.2 + */ + private static boolean equalsIncludingNaN(double x, double y) { + return (x != x || y != y) ? !(x != x ^ y != y) : Precision.equals(x, y, 1); + } + + +} + + http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-quaternion/LICENSE.txt ---------------------------------------------------------------------- diff --git a/commons-numbers-quaternion/LICENSE.txt b/commons-numbers-quaternion/LICENSE.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/commons-numbers-quaternion/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-quaternion/NOTICE.txt ---------------------------------------------------------------------- diff --git a/commons-numbers-quaternion/NOTICE.txt b/commons-numbers-quaternion/NOTICE.txt new file mode 100644 index 0000000..ea6ae07 --- /dev/null +++ b/commons-numbers-quaternion/NOTICE.txt @@ -0,0 +1,6 @@ +Apache Commons Numbers +Copyright 2001-2016 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-quaternion/README.md ---------------------------------------------------------------------- diff --git a/commons-numbers-quaternion/README.md b/commons-numbers-quaternion/README.md new file mode 100644 index 0000000..ab9f829 --- /dev/null +++ b/commons-numbers-quaternion/README.md @@ -0,0 +1,98 @@ + + +Apache Commons Numbers Quaternion +=================== + +Quaternion numbers. + +Documentation +------------- + +More information can be found on the [homepage](https://commons.apache.org/proper/commons-numbers). +The [JavaDoc](https://commons.apache.org/proper/commons-numbers/javadocs/api-release) can be browsed. +Questions related to the usage of Apache Commons Numbers Quaternion should be posted to the [user mailing list][ml]. + +Where can I get the latest release? +----------------------------------- +You can download source and binaries from our [download page](https://commons.apache.org/proper/commons-numbers/download_numbers.cgi). + +Alternatively you can pull it from the central Maven repositories: + +```xml + + org.apache.commons + commons-numbers-quaternion + 1.0 + +``` + +Contributing +------------ + +We accept PRs via github. The [developer mailing list][ml] is the main channel of communication for contributors. +There are some guidelines which will make applying PRs easier for us: ++ No tabs! Please use spaces for indentation. ++ Respect the code style. ++ Create minimal diffs - disable on save actions like reformat source code or organize imports. If you feel the source code should be reformatted create a separate PR for this change. ++ Provide JUnit tests for your changes and make sure your changes don't break any existing tests by running ```mvn clean test```. + +If you plan to contribute on a regular basis, please consider filing a [contributor license agreement](https://www.apache.org/licenses/#clas). +You can learn more about contributing via GitHub in our [contribution guidelines](CONTRIBUTING.md). + +License +------- +Code is under the [Apache Licence v2](https://www.apache.org/licenses/LICENSE-2.0.txt). + +Donations +--------- +You like Apache Commons Numbers Quaternion? Then [donate back to the ASF](https://www.apache.org/foundation/contributing.html) to support the development. + +Additional Resources +-------------------- + ++ [Apache Commons Homepage](https://commons.apache.org/) ++ [Apache Bugtracker (JIRA)](https://issues.apache.org/jira/) ++ [Apache Commons Twitter Account](https://twitter.com/ApacheCommons) ++ #apachecommons IRC channel on freenode.org + +[ml]:https://commons.apache.org/mail-lists.html http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-quaternion/pom.xml ---------------------------------------------------------------------- diff --git a/commons-numbers-quaternion/pom.xml b/commons-numbers-quaternion/pom.xml new file mode 100644 index 0000000..76a59e3 --- /dev/null +++ b/commons-numbers-quaternion/pom.xml @@ -0,0 +1,53 @@ + + + + 4.0.0 + + + org.apache.commons + commons-numbers-parent + 1.0-SNAPSHOT + + + org.apache.commons + commons-numbers-quaternion + 1.0-SNAPSHOT + Apache Commons Numbers Quaternion + + Quaternion numbers. + + + + org.apache.commons.numbers.quaternion + + org.apache.commons.numbers.quaternion + + ${basedir}/.. + + + + + org.apache.commons + commons-numbers-core + 1.0-SNAPSHOT + + + + http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-quaternion/src/main/java/org/apache/commons/numbers/quaternion/Quaternion.java ---------------------------------------------------------------------- diff --git a/commons-numbers-quaternion/src/main/java/org/apache/commons/numbers/quaternion/Quaternion.java b/commons-numbers-quaternion/src/main/java/org/apache/commons/numbers/quaternion/Quaternion.java new file mode 100644 index 0000000..1722f9b --- /dev/null +++ b/commons-numbers-quaternion/src/main/java/org/apache/commons/numbers/quaternion/Quaternion.java @@ -0,0 +1,455 @@ +/* + * 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 java.util.Arrays; +import java.io.Serializable; +import org.apache.commons.numbers.core.Precision; + +/** + * This class implements + * quaternions (Hamilton's hypercomplex numbers). + * + *

Instance of this class are guaranteed to be immutable.

+ */ +public final class Quaternion implements Serializable { + /** Serializable version identifier. */ + private static final long serialVersionUID = 20170118L; + /** Error message. */ + private static final String ZERO_NORM_MSG = "Norm is zero"; + + /** Identity quaternion. */ + public static final Quaternion IDENTITY = new Quaternion(1, 0, 0, 0); + /** Zero quaternion. */ + public static final Quaternion ZERO = new Quaternion(0, 0, 0, 0); + /** i */ + public static final Quaternion I = new Quaternion(0, 1, 0, 0); + /** j */ + public static final Quaternion J = new Quaternion(0, 0, 1, 0); + /** k */ + public static final Quaternion K = new Quaternion(0, 0, 0, 1); + + /** First component (scalar part). */ + private final double q0; + /** Second component (first vector part). */ + private final double q1; + /** Third component (second vector part). */ + private final double q2; + /** Fourth component (third vector part). */ + private final double q3; + + /** + * Builds a quaternion from its components. + * + * @param a Scalar component. + * @param b First vector component. + * @param c Second vector component. + * @param d Third vector component. + */ + public Quaternion(final double a, + final double b, + final double c, + final double d) { + q0 = a; + q1 = b; + q2 = c; + q3 = d; + } + + /** + * Builds a quaternion from scalar and vector parts. + * + * @param scalar Scalar part of the quaternion. + * @param v Components of the vector part of the quaternion. + * + * @throws IllegalArgumentException if the array length is not 3. + */ + public Quaternion(final double scalar, + final double[] v) { + if (v.length != 3) { + throw new IllegalArgumentException("Size of array must be 3"); + } + + q0 = scalar; + q1 = v[0]; + q2 = v[1]; + q3 = v[2]; + } + + /** + * Builds a pure quaternion from a vector (assuming that the scalar + * part is zero). + * + * @param v Components of the vector part of the pure quaternion. + */ + public Quaternion(final double[] v) { + this(0, v); + } + + /** + * Returns the conjugate quaternion of the instance. + * + * @return the conjugate quaternion + */ + public Quaternion getConjugate() { + return new Quaternion(q0, -q1, -q2, -q3); + } + + /** + * Returns the Hamilton product of two quaternions. + * + * @param q1 First quaternion. + * @param q2 Second quaternion. + * @return the product {@code q1} and {@code q2}, in that order. + */ + public static Quaternion multiply(final Quaternion q1, + final Quaternion q2) { + // Components of the first quaternion. + final double q1a = q1.q0; + final double q1b = q1.q1; + final double q1c = q1.q2; + final double q1d = q1.q3; + + // Components of the second quaternion. + final double q2a = q2.q0; + final double q2b = q2.q1; + final double q2c = q2.q2; + final double q2d = q2.q3; + + // Components of the product. + final double w = q1a * q2a - q1b * q2b - q1c * q2c - q1d * q2d; + final double x = q1a * q2b + q1b * q2a + q1c * q2d - q1d * q2c; + final double y = q1a * q2c - q1b * q2d + q1c * q2a + q1d * q2b; + final double z = q1a * q2d + q1b * q2c - q1c * q2b + q1d * q2a; + + return new Quaternion(w, x, y, z); + } + + /** + * Returns the Hamilton product of the instance by a quaternion. + * + * @param q Quaternion. + * @return the product of this instance with {@code q}, in that order. + */ + public Quaternion multiply(final Quaternion q) { + return multiply(this, q); + } + + /** + * Computes the sum of two quaternions. + * + * @param q1 Quaternion. + * @param q2 Quaternion. + * @return the sum of {@code q1} and {@code q2}. + */ + public static Quaternion add(final Quaternion q1, + final Quaternion q2) { + return new Quaternion(q1.q0 + q2.q0, + q1.q1 + q2.q1, + q1.q2 + q2.q2, + q1.q3 + q2.q3); + } + + /** + * Computes the sum of the instance and another quaternion. + * + * @param q Quaternion. + * @return the sum of this instance and {@code q} + */ + public Quaternion add(final Quaternion q) { + return add(this, q); + } + + /** + * Subtracts two quaternions. + * + * @param q1 First Quaternion. + * @param q2 Second quaternion. + * @return the difference between {@code q1} and {@code q2}. + */ + public static Quaternion subtract(final Quaternion q1, + final Quaternion q2) { + return new Quaternion(q1.q0 - q2.q0, + q1.q1 - q2.q1, + q1.q2 - q2.q2, + q1.q3 - q2.q3); + } + + /** + * Subtracts a quaternion from the instance. + * + * @param q Quaternion. + * @return the difference between this instance and {@code q}. + */ + public Quaternion subtract(final Quaternion q) { + return subtract(this, q); + } + + /** + * Computes the dot-product of two quaternions. + * + * @param q1 Quaternion. + * @param q2 Quaternion. + * @return the dot product of {@code q1} and {@code q2}. + */ + public static double dotProduct(final Quaternion q1, + final Quaternion q2) { + return q1.q0 * q2.q0 + + q1.q1 * q2.q1 + + q1.q2 * q2.q2 + + q1.q3 * q2.q3; + } + + /** + * Computes the dot-product of the instance by a quaternion. + * + * @param q Quaternion. + * @return the dot product of this instance and {@code q}. + */ + public double dotProduct(final Quaternion q) { + return dotProduct(this, q); + } + + /** + * Computes the norm of the quaternion. + * + * @return the norm. + */ + public double getNorm() { + return Math.sqrt(q0 * q0 + + q1 * q1 + + q2 * q2 + + q3 * q3); + } + + /** + * Computes the normalized quaternion (the versor of the instance). + * The norm of the quaternion must not be zero. + * + * @return a normalized quaternion. + * @throws IllegalStateException if the norm of the quaternion is zero. + */ + public Quaternion normalize() { + final double norm = getNorm(); + + if (norm < Precision.SAFE_MIN) { + throw new IllegalStateException(ZERO_NORM_MSG); + } + + return new Quaternion(q0 / norm, + q1 / norm, + q2 / norm, + q3 / norm); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other instanceof Quaternion) { + final Quaternion q = (Quaternion) other; + return q0 == q.q0 && + q1 == q.q1 && + q2 == q.q2 && + q3 == q.q3; + } + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Arrays.hashCode(new double[] { q0, q1, q2, q3 }); + } + + /** + * Checks whether this instance is equal to another quaternion + * within a given tolerance. + * + * @param q Quaternion with which to compare the current quaternion. + * @param eps Tolerance. + * @return {@code true} if the each of the components are equal + * within the allowed absolute error. + */ + public boolean equals(final Quaternion q, + final double eps) { + return Precision.equals(q0, q.q0, eps) && + Precision.equals(q1, q.q1, eps) && + Precision.equals(q2, q.q2, eps) && + Precision.equals(q3, q.q3, eps); + } + + /** + * Checks whether the instance is a unit quaternion within a given + * tolerance. + * + * @param eps Tolerance (absolute error). + * @return {@code true} if the norm is 1 within the given tolerance, + * {@code false} otherwise + */ + public boolean isUnitQuaternion(double eps) { + return Precision.equals(getNorm(), 1d, eps); + } + + /** + * Checks whether the instance is a pure quaternion within a given + * tolerance. + * + * @param eps Tolerance (absolute error). + * @return {@code true} if the scalar part of the quaternion is zero. + */ + public boolean isPureQuaternion(double eps) { + return Math.abs(q0) <= eps; + } + + /** + * Returns the polar form of the quaternion. + * + * @return the unit quaternion with positive scalar part. + */ + public Quaternion getPositivePolarForm() { + if (q0 < 0) { + final Quaternion unitQ = normalize(); + // The quaternion of rotation (normalized quaternion) q and -q + // are equivalent (i.e. represent the same rotation). + return new Quaternion(-unitQ.q0, + -unitQ.q1, + -unitQ.q2, + -unitQ.q3); + } else { + return this.normalize(); + } + } + + /** + * Returns the inverse of this instance. + * The norm of the quaternion must not be zero. + * + * @return the inverse. + * @throws IllegalArgumentException if the norm (squared) of the quaternion is zero. + */ + public Quaternion getInverse() { + final double squareNorm = q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3; + if (squareNorm < Precision.SAFE_MIN) { + throw new IllegalStateException(ZERO_NORM_MSG); + } + + return new Quaternion(q0 / squareNorm, + -q1 / squareNorm, + -q2 / squareNorm, + -q3 / squareNorm); + } + + /** + * Gets the first component of the quaternion (scalar part). + * + * @return the scalar part. + */ + public double getQ0() { + return q0; + } + + /** + * Gets the second component of the quaternion (first component + * of the vector part). + * + * @return the first component of the vector part. + */ + public double getQ1() { + return q1; + } + + /** + * Gets the third component of the quaternion (second component + * of the vector part). + * + * @return the second component of the vector part. + */ + public double getQ2() { + return q2; + } + + /** + * Gets the fourth component of the quaternion (third component + * of the vector part). + * + * @return the third component of the vector part. + */ + public double getQ3() { + return q3; + } + + /** + * Gets the scalar part of the quaternion. + * + * @return the scalar part. + * @see #getQ0() + */ + public double getScalarPart() { + return q0; + } + + /** + * Gets the three components of the vector part of the quaternion. + * + * @return the vector part. + * @see #getQ1() + * @see #getQ2() + * @see #getQ3() + */ + public double[] getVectorPart() { + return new double[] { q1, q2, q3 }; + } + + /** + * Multiplies the instance by a scalar. + * + * @param alpha Scalar factor. + * @return a scaled quaternion. + */ + public Quaternion multiply(final double alpha) { + return new Quaternion(alpha * q0, + alpha * q1, + alpha * q2, + alpha * q3); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final String sp = " "; + final StringBuilder s = new StringBuilder(); + s.append("[") + .append(q0).append(sp) + .append(q1).append(sp) + .append(q2).append(sp) + .append(q3) + .append("]"); + + return s.toString(); + } +} http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-quaternion/src/main/java/org/apache/commons/numbers/quaternion/package-info.java ---------------------------------------------------------------------- diff --git a/commons-numbers-quaternion/src/main/java/org/apache/commons/numbers/quaternion/package-info.java b/commons-numbers-quaternion/src/main/java/org/apache/commons/numbers/quaternion/package-info.java new file mode 100644 index 0000000..a8696ec --- /dev/null +++ b/commons-numbers-quaternion/src/main/java/org/apache/commons/numbers/quaternion/package-info.java @@ -0,0 +1,20 @@ +/* + * 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. + */ +/** + * Quaternion number type. + */ +package org.apache.commons.numbers.quaternion;