http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java ---------------------------------------------------------------------- diff --git a/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java new file mode 100644 index 0000000..70dc4c1 --- /dev/null +++ b/commons-numbers-complex/src/test/java/org/apache/commons/numbers/complex/TestUtils.java @@ -0,0 +1,409 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.numbers.complex; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.apache.commons.numbers.complex.Complex; + +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 real and imaginary parts of the two complex arguments + * are exactly the same. Also ensures that NaN / infinite components match. + */ + public static void assertSame(Complex expected, Complex actual) { + assertSame(expected.getReal(), actual.getReal()); + assertSame(expected.getImaginary(), actual.getImaginary()); + } + + /** + * Verifies that real and imaginary parts of the two complex arguments + * differ by at most delta. Also ensures that NaN / infinite components match. + */ + public static void assertEquals(Complex expected, Complex actual, double delta) { + Assert.assertEquals(expected.getReal(), actual.getReal(), delta); + Assert.assertEquals(expected.getImaginary(), actual.getImaginary(), delta); + } + + /** + * 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 z. + * + * @param msg message to return with failure + * @param values complex array to search + * @param z value sought + * @param epsilon tolerance + */ + public static void assertContains(String msg, Complex[] values, + Complex z, double epsilon) { + for (Complex value : values) { + if (Precision.equals(value.getReal(), z.getReal(), epsilon) && + Precision.equals(value.getImaginary(), z.getImaginary(), epsilon)) { + return; + } + } + Assert.fail(msg + " Unable to find " + z); + } + + /** + * Fails iff values does not contain a number within epsilon of z. + * + * @param values complex array to search + * @param z value sought + * @param epsilon tolerance + */ + public static void assertContains(Complex[] values, + Complex z, double epsilon) { + assertContains(null, values, z, epsilon); + } + + /** + * 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()); + } + } + + /** verifies that two arrays are close (sup norm) */ + public static void assertEquals(String msg, Complex[] expected, Complex[] 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].getReal(), observed[i].getReal(), tolerance)) { + failure = true; + out.append("\n Real elements at index "); + out.append(i); + out.append(" differ. "); + out.append(" expected = "); + out.append(expected[i].getReal()); + out.append(" observed = "); + out.append(observed[i].getReal()); + } + if (!equalsIncludingNaN(expected[i].getImaginary(), observed[i].getImaginary(), tolerance)) { + failure = true; + out.append("\n Imaginary elements at index "); + out.append(i); + out.append(" differ. "); + out.append(" expected = "); + out.append(expected[i].getImaginary()); + out.append(" observed = "); + out.append(observed[i].getImaginary()); + } + } + 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 <returned value> 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-core/LICENSE.txt ---------------------------------------------------------------------- diff --git a/commons-numbers-core/LICENSE.txt b/commons-numbers-core/LICENSE.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/commons-numbers-core/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-core/NOTICE.txt ---------------------------------------------------------------------- diff --git a/commons-numbers-core/NOTICE.txt b/commons-numbers-core/NOTICE.txt new file mode 100644 index 0000000..ea6ae07 --- /dev/null +++ b/commons-numbers-core/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-core/README.md ---------------------------------------------------------------------- diff --git a/commons-numbers-core/README.md b/commons-numbers-core/README.md new file mode 100644 index 0000000..0701bed --- /dev/null +++ b/commons-numbers-core/README.md @@ -0,0 +1,98 @@ +<!--- + 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. +--> +<!--- + +======================================================================+ + |**** ****| + |**** THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN ****| + |**** DO NOT EDIT DIRECTLY ****| + |**** ****| + +======================================================================+ + | TEMPLATE FILE: readme-md-template.md | + | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates | + +======================================================================+ + | | + | 1) Re-generate using: mvn commons:readme-md | + | | + | 2) Set the following properties in the component's pom: | + | - commons.componentid (required, alphabetic, lower case) | + | - commons.release.version (required) | + | | + | 3) Example Properties | + | | + | <properties> | + | <commons.componentid>math</commons.componentid> | + | <commons.release.version>1.2</commons.release.version> | + | </properties> | + | | + +======================================================================+ +---> +Apache Commons Numbers Core +=================== + +Basic utilities. + +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 Core 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 +<dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-numbers-core</artifactId> + <version>1.0</version> +</dependency> +``` + +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 Core? 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-core/pom.xml ---------------------------------------------------------------------- diff --git a/commons-numbers-core/pom.xml b/commons-numbers-core/pom.xml new file mode 100644 index 0000000..39dbdcb --- /dev/null +++ b/commons-numbers-core/pom.xml @@ -0,0 +1,46 @@ +<?xml version="1.0"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.commons</groupId> + <artifactId>commons-numbers-parent</artifactId> + <version>1.0-SNAPSHOT</version> + </parent> + + <groupId>org.apache.commons</groupId> + <artifactId>commons-numbers-core</artifactId> + <version>1.0-SNAPSHOT</version> + <name>Apache Commons Numbers Core</name> + + <description>Basic utilities.</description> + + <properties> + <!-- This value must reflect the current name of the base package. --> + <commons.osgi.symbolicName>org.apache.commons.numbers.core</commons.osgi.symbolicName> + <!-- OSGi --> + <commons.osgi.export>org.apache.commons.numbers.core</commons.osgi.export> + <!-- Workaround to avoid duplicating config files. --> + <numbers.parent.dir>${basedir}/..</numbers.parent.dir> + </properties> + + +</project> http://git-wip-us.apache.org/repos/asf/commons-numbers/blob/c4541327/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/Precision.java ---------------------------------------------------------------------- diff --git a/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/Precision.java b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/Precision.java new file mode 100644 index 0000000..e71e1e4 --- /dev/null +++ b/commons-numbers-core/src/main/java/org/apache/commons/numbers/core/Precision.java @@ -0,0 +1,598 @@ +/* + * 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; + +/** + * Utilities for comparing numbers. + */ +public class Precision { + /** + * <p> + * Largest double-precision floating-point number such that + * {@code 1 + EPSILON} is numerically equal to 1. This value is an upper + * bound on the relative error due to rounding real numbers to double + * precision floating-point numbers. + * </p> + * <p> + * In IEEE 754 arithmetic, this is 2<sup>-53</sup>. + * </p> + * + * @see <a href="http://en.wikipedia.org/wiki/Machine_epsilon">Machine epsilon</a> + */ + public static final double EPSILON; + + /** + * Safe minimum, such that {@code 1 / SAFE_MIN} does not overflow. + * In IEEE 754 arithmetic, this is also the smallest normalized + * number 2<sup>-1022</sup>. + */ + public static final double SAFE_MIN; + + /** Exponent offset in IEEE754 representation. */ + private static final long EXPONENT_OFFSET = 1023l; + + /** Offset to order signed double numbers lexicographically. */ + private static final long SGN_MASK = 0x8000000000000000L; + /** Offset to order signed double numbers lexicographically. */ + private static final int SGN_MASK_FLOAT = 0x80000000; + /** Positive zero. */ + private static final double POSITIVE_ZERO = 0d; + /** Positive zero bits. */ + private static final long POSITIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(+0.0); + /** Negative zero bits. */ + private static final long NEGATIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(-0.0); + /** Positive zero bits. */ + private static final int POSITIVE_ZERO_FLOAT_BITS = Float.floatToRawIntBits(+0.0f); + /** Negative zero bits. */ + private static final int NEGATIVE_ZERO_FLOAT_BITS = Float.floatToRawIntBits(-0.0f); + + static { + /* + * This was previously expressed as = 0x1.0p-53; + * However, OpenJDK (Sparc Solaris) cannot handle such small + * constants: MATH-721 + */ + EPSILON = Double.longBitsToDouble((EXPONENT_OFFSET - 53l) << 52); + + /* + * This was previously expressed as = 0x1.0p-1022; + * However, OpenJDK (Sparc Solaris) cannot handle such small + * constants: MATH-721 + */ + SAFE_MIN = Double.longBitsToDouble((EXPONENT_OFFSET - 1022l) << 52); + } + + /** + * Private constructor. + */ + private Precision() {} + + /** + * Compares two numbers given some amount of allowed error. + * The returned value is + * <ul> + * <li> + * 0 if {@link #equals(double,double,double) equals(x, y, eps)}, + * </li> + * <li> + * negative if !{@link #equals(double,double,double) equals(x, y, eps)} and {@code x < y}, + * </li> + * <li> + * positive if !{@link #equals(double,double,double) equals(x, y, eps)} and {@code x > y} or + * either argument is {@code NaN}. + * </li> + * </ul> + * + * @param x First value. + * @param y Second value. + * @param eps Allowed error when checking for equality. + * @return 0 if the value are considered equal, -1 if the first is smaller than + * the second, 1 is the first is larger than the second. + */ + public static int compareTo(double x, double y, double eps) { + if (equals(x, y, eps)) { + return 0; + } else if (x < y) { + return -1; + } + return 1; + } + + /** + * Compares two numbers given some amount of allowed error. + * Two float numbers are considered equal if there are {@code (maxUlps - 1)} + * (or fewer) floating point numbers between them, i.e. two adjacent floating + * point numbers are considered equal. + * Adapted from + * <a href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/"> + * Bruce Dawson</a>. Returns {@code false} if either of the arguments is NaN. + * The returned value is + * <ul> + * <li> + * zero if {@link #equals(double,double,int) equals(x, y, maxUlps)}, + * </li> + * <li> + * negative if !{@link #equals(double,double,int) equals(x, y, maxUlps)} and {@code x < y}, + * </li> + * <li> + * positive if !{@link #equals(double,double,int) equals(x, y, maxUlps)} and {@code x > y} + * or either argument is {@code NaN}. + * </li> + * </ul> + * + * @param x First value. + * @param y Second value. + * @param maxUlps {@code (maxUlps - 1)} is the number of floating point + * values between {@code x} and {@code y}. + * @return 0 if the value are considered equal, -1 if the first is smaller than + * the second, 1 is the first is larger than the second. + */ + public static int compareTo(final double x, final double y, final int maxUlps) { + if (equals(x, y, maxUlps)) { + return 0; + } else if (x < y) { + return -1; + } + return 1; + } + + /** + * Returns true iff they are equal as defined by + * {@link #equals(float,float,int) equals(x, y, 1)}. + * + * @param x first value + * @param y second value + * @return {@code true} if the values are equal. + */ + public static boolean equals(float x, float y) { + return equals(x, y, 1); + } + + /** + * Returns true if both arguments are NaN or they are + * equal as defined by {@link #equals(float,float) equals(x, y, 1)}. + * + * @param x first value + * @param y second value + * @return {@code true} if the values are equal or both are NaN. + */ + public static boolean equalsIncludingNaN(float x, float y) { + return (x != x || y != y) ? !(x != x ^ y != y) : equals(x, y, 1); + } + + /** + * Returns true if the arguments are equal or within the range of allowed + * error (inclusive). Returns {@code false} if either of the arguments + * is NaN. + * + * @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. + */ + public static boolean equals(float x, float y, float eps) { + return equals(x, y, 1) || Math.abs(y - x) <= eps; + } + + /** + * 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. + */ + public static boolean equalsIncludingNaN(float x, float y, float eps) { + return equalsIncludingNaN(x, y) || (Math.abs(y - x) <= eps); + } + + /** + * Returns true if the arguments are equal or within the range of allowed + * error (inclusive). + * Two float numbers are considered equal if there are {@code (maxUlps - 1)} + * (or fewer) floating point numbers between them, i.e. two adjacent floating + * point numbers are considered equal. + * Adapted from <a + * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/"> + * Bruce Dawson</a>. Returns {@code false} if either of the arguments is NaN. + * + * @param x first value + * @param y second value + * @param maxUlps {@code (maxUlps - 1)} is the number of floating point + * values between {@code x} and {@code y}. + * @return {@code true} if there are fewer than {@code maxUlps} floating + * point values between {@code x} and {@code y}. + */ + public static boolean equals(final float x, final float y, final int maxUlps) { + + final int xInt = Float.floatToRawIntBits(x); + final int yInt = Float.floatToRawIntBits(y); + + final boolean isEqual; + if (((xInt ^ yInt) & SGN_MASK_FLOAT) == 0) { + // number have same sign, there is no risk of overflow + isEqual = Math.abs(xInt - yInt) <= maxUlps; + } else { + // number have opposite signs, take care of overflow + final int deltaPlus; + final int deltaMinus; + if (xInt < yInt) { + deltaPlus = yInt - POSITIVE_ZERO_FLOAT_BITS; + deltaMinus = xInt - NEGATIVE_ZERO_FLOAT_BITS; + } else { + deltaPlus = xInt - POSITIVE_ZERO_FLOAT_BITS; + deltaMinus = yInt - NEGATIVE_ZERO_FLOAT_BITS; + } + + if (deltaPlus > maxUlps) { + isEqual = false; + } else { + isEqual = deltaMinus <= (maxUlps - deltaPlus); + } + + } + + return isEqual && !Float.isNaN(x) && !Float.isNaN(y); + + } + + /** + * Returns true if the arguments are both NaN or if they are equal as defined + * by {@link #equals(float,float,int) equals(x, y, maxUlps)}. + * + * @param x first value + * @param y second value + * @param maxUlps {@code (maxUlps - 1)} is the number of floating point + * values between {@code x} and {@code y}. + * @return {@code true} if both arguments are NaN or if there are less than + * {@code maxUlps} floating point values between {@code x} and {@code y}. + */ + public static boolean equalsIncludingNaN(float x, float y, int maxUlps) { + return (x != x || y != y) ? !(x != x ^ y != y) : equals(x, y, maxUlps); + } + + /** + * Returns true iff they are equal as defined by + * {@link #equals(double,double,int) equals(x, y, 1)}. + * + * @param x first value + * @param y second value + * @return {@code true} if the values are equal. + */ + public static boolean equals(double x, double y) { + return equals(x, y, 1); + } + + /** + * 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. + */ + public static boolean equalsIncludingNaN(double x, double y) { + return (x != x || y != y) ? !(x != x ^ y != y) : equals(x, y, 1); + } + + /** + * Returns {@code true} if there is no double value strictly between the + * arguments or the difference between them is within the range of allowed + * error (inclusive). Returns {@code false} if either of the arguments + * is NaN. + * + * @param x First value. + * @param y Second value. + * @param eps Amount of allowed absolute error. + * @return {@code true} if the values are two adjacent floating point + * numbers or they are within range of each other. + */ + public static boolean equals(double x, double y, double eps) { + return equals(x, y, 1) || Math.abs(y - x) <= eps; + } + + /** + * Returns {@code true} if there is no double value strictly between the + * arguments or the relative difference between them is less than or equal + * to the given tolerance. Returns {@code false} if either of the arguments + * is NaN. + * + * @param x First value. + * @param y Second value. + * @param eps Amount of allowed relative error. + * @return {@code true} if the values are two adjacent floating point + * numbers or they are within range of each other. + */ + public static boolean equalsWithRelativeTolerance(double x, double y, double eps) { + if (equals(x, y, 1)) { + return true; + } + + final double absoluteMax = Math.max(Math.abs(x), Math.abs(y)); + final double relativeDifference = Math.abs((x - y) / absoluteMax); + + return relativeDifference <= eps; + } + + /** + * 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. + */ + public static boolean equalsIncludingNaN(double x, double y, double eps) { + return equalsIncludingNaN(x, y) || (Math.abs(y - x) <= eps); + } + + /** + * Returns true if the arguments are equal or within the range of allowed + * error (inclusive). + * <p> + * Two float numbers are considered equal if there are {@code (maxUlps - 1)} + * (or fewer) floating point numbers between them, i.e. two adjacent + * floating point numbers are considered equal. + * </p> + * <p> + * Adapted from <a + * href="http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/"> + * Bruce Dawson</a>. Returns {@code false} if either of the arguments is NaN. + * </p> + * + * @param x first value + * @param y second value + * @param maxUlps {@code (maxUlps - 1)} is the number of floating point + * values between {@code x} and {@code y}. + * @return {@code true} if there are fewer than {@code maxUlps} floating + * point values between {@code x} and {@code y}. + */ + public static boolean equals(final double x, final double y, final int maxUlps) { + + final long xInt = Double.doubleToRawLongBits(x); + final long yInt = Double.doubleToRawLongBits(y); + + final boolean isEqual; + if (((xInt ^ yInt) & SGN_MASK) == 0l) { + // number have same sign, there is no risk of overflow + isEqual = Math.abs(xInt - yInt) <= maxUlps; + } else { + // number have opposite signs, take care of overflow + final long deltaPlus; + final long deltaMinus; + if (xInt < yInt) { + deltaPlus = yInt - POSITIVE_ZERO_DOUBLE_BITS; + deltaMinus = xInt - NEGATIVE_ZERO_DOUBLE_BITS; + } else { + deltaPlus = xInt - POSITIVE_ZERO_DOUBLE_BITS; + deltaMinus = yInt - NEGATIVE_ZERO_DOUBLE_BITS; + } + + if (deltaPlus > maxUlps) { + isEqual = false; + } else { + isEqual = deltaMinus <= (maxUlps - deltaPlus); + } + + } + + return isEqual && !Double.isNaN(x) && !Double.isNaN(y); + + } + + /** + * Returns true if both arguments are NaN or if they are equal as defined + * by {@link #equals(double,double,int) equals(x, y, maxUlps)}. + * + * @param x first value + * @param y second value + * @param maxUlps {@code (maxUlps - 1)} is the number of floating point + * values between {@code x} and {@code y}. + * @return {@code true} if both arguments are NaN or if there are less than + * {@code maxUlps} floating point values between {@code x} and {@code y}. + */ + public static boolean equalsIncludingNaN(double x, double y, int maxUlps) { + return (x != x || y != y) ? !(x != x ^ y != y) : equals(x, y, maxUlps); + } + + /** + * Rounds the given value to the specified number of decimal places. + * The value is rounded using the {@link BigDecimal#ROUND_HALF_UP} method. + * + * @param x Value to round. + * @param scale Number of digits to the right of the decimal point. + * @return the rounded value. + */ + public static double round(double x, int scale) { + return round(x, scale, BigDecimal.ROUND_HALF_UP); + } + + /** + * Rounds the given value to the specified number of decimal places. + * The value is rounded using the given method which is any method defined + * in {@link BigDecimal}. + * If {@code x} is infinite or {@code NaN}, then the value of {@code x} is + * returned unchanged, regardless of the other parameters. + * + * @param x Value to round. + * @param scale Number of digits to the right of the decimal point. + * @param roundingMethod Rounding method as defined in {@link BigDecimal}. + * @return the rounded value. + * @throws ArithmeticException if {@code roundingMethod == ROUND_UNNECESSARY} + * and the specified scaling operation would require rounding. + * @throws IllegalArgumentException if {@code roundingMethod} does not + * represent a valid rounding mode. + */ + public static double round(double x, int scale, int roundingMethod) { + try { + final double rounded = (new BigDecimal(Double.toString(x)) + .setScale(scale, roundingMethod)) + .doubleValue(); + // MATH-1089: negative values rounded to zero should result in negative zero + return rounded == POSITIVE_ZERO ? POSITIVE_ZERO * x : rounded; + } catch (NumberFormatException ex) { + if (Double.isInfinite(x)) { + return x; + } else { + return Double.NaN; + } + } + } + + /** + * Rounds the given value to the specified number of decimal places. + * The value is rounded using the {@link BigDecimal#ROUND_HALF_UP} method. + * + * @param x Value to round. + * @param scale Number of digits to the right of the decimal point. + * @return the rounded value. + */ + public static float round(float x, int scale) { + return round(x, scale, BigDecimal.ROUND_HALF_UP); + } + + /** + * Rounds the given value to the specified number of decimal places. + * The value is rounded using the given method which is any method defined + * in {@link BigDecimal}. + * + * @param x Value to round. + * @param scale Number of digits to the right of the decimal point. + * @param roundingMethod Rounding method as defined in {@link BigDecimal}. + * @return the rounded value. + * @throws ArithmeticException if an exact operation is required but result is not exact + * @throws IllegalArgumentException if {@code roundingMethod} is not a valid rounding method. + */ + public static float round(float x, int scale, int roundingMethod) { + final float sign = Math.copySign(1f, x); + final float factor = (float) Math.pow(10.0f, scale) * sign; + return (float) roundUnscaled(x * factor, sign, roundingMethod) / factor; + } + + /** + * Rounds the given non-negative value to the "nearest" integer. Nearest is + * determined by the rounding method specified. Rounding methods are defined + * in {@link BigDecimal}. + * + * @param unscaled Value to round. + * @param sign Sign of the original, scaled value. + * @param roundingMethod Rounding method, as defined in {@link BigDecimal}. + * @return the rounded value. + * @throws ArithmeticException if an exact operation is required but result is not exact + * @throws IllegalArgumentException if {@code roundingMethod} is not a valid rounding method. + */ + private static double roundUnscaled(double unscaled, + double sign, + int roundingMethod) { + switch (roundingMethod) { + case BigDecimal.ROUND_CEILING : + if (sign == -1) { + unscaled = Math.floor(Math.nextAfter(unscaled, Double.NEGATIVE_INFINITY)); + } else { + unscaled = Math.ceil(Math.nextAfter(unscaled, Double.POSITIVE_INFINITY)); + } + break; + case BigDecimal.ROUND_DOWN : + unscaled = Math.floor(Math.nextAfter(unscaled, Double.NEGATIVE_INFINITY)); + break; + case BigDecimal.ROUND_FLOOR : + if (sign == -1) { + unscaled = Math.ceil(Math.nextAfter(unscaled, Double.POSITIVE_INFINITY)); + } else { + unscaled = Math.floor(Math.nextAfter(unscaled, Double.NEGATIVE_INFINITY)); + } + break; + case BigDecimal.ROUND_HALF_DOWN : { + unscaled = Math.nextAfter(unscaled, Double.NEGATIVE_INFINITY); + double fraction = unscaled - Math.floor(unscaled); + if (fraction > 0.5) { + unscaled = Math.ceil(unscaled); + } else { + unscaled = Math.floor(unscaled); + } + break; + } + case BigDecimal.ROUND_HALF_EVEN : { + double fraction = unscaled - Math.floor(unscaled); + if (fraction > 0.5) { + unscaled = Math.ceil(unscaled); + } else if (fraction < 0.5) { + unscaled = Math.floor(unscaled); + } else { + // The following equality test is intentional and needed for rounding purposes + if (Math.floor(unscaled) / 2.0 == Math.floor(Math.floor(unscaled) / 2.0)) { // even + unscaled = Math.floor(unscaled); + } else { // odd + unscaled = Math.ceil(unscaled); + } + } + break; + } + case BigDecimal.ROUND_HALF_UP : { + unscaled = Math.nextAfter(unscaled, Double.POSITIVE_INFINITY); + double fraction = unscaled - Math.floor(unscaled); + if (fraction >= 0.5) { + unscaled = Math.ceil(unscaled); + } else { + unscaled = Math.floor(unscaled); + } + break; + } + case BigDecimal.ROUND_UNNECESSARY : + if (unscaled != Math.floor(unscaled)) { + throw new ArithmeticException(); + } + break; + case BigDecimal.ROUND_UP : + // do not round if the discarded fraction is equal to zero + if (unscaled != Math.floor(unscaled)) { + unscaled = Math.ceil(Math.nextAfter(unscaled, Double.POSITIVE_INFINITY)); + } + break; + default : + throw new IllegalArgumentException("Unhandled rounding method: " + roundingMethod); + } + return unscaled; + } + + + /** + * Computes a number {@code delta} close to {@code originalDelta} with + * the property that <pre><code> + * x + delta - x + * </code></pre> + * is exactly machine-representable. + * This is useful when computing numerical derivatives, in order to reduce + * roundoff errors. + * + * @param x Value. + * @param originalDelta Offset value. + * @return a number {@code delta} so that {@code x + delta} and {@code x} + * differ by a representable floating number. + */ + public static double representableDelta(double x, + double originalDelta) { + return x + originalDelta - x; + } +}
