This is an automated email from the ASF dual-hosted git repository. erans pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-numbers.git
The following commit(s) were added to refs/heads/master by this push: new 00f9f4e NUMBERS-157: Reduce to primary interval. 00f9f4e is described below commit 00f9f4ec702a428ac0e244a424a16e19a8afc71a Author: Gilles Sadowski <gillese...@gmail.com> AuthorDate: Wed May 26 16:49:56 2021 +0200 NUMBERS-157: Reduce to primary interval. Functionality ported from "Commons Math". --- .../org/apache/commons/numbers/angle/Reduce.java | 49 +++++++++ .../apache/commons/numbers/angle/ReduceTest.java | 110 +++++++++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/Reduce.java b/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/Reduce.java new file mode 100644 index 0000000..7123c2d --- /dev/null +++ b/commons-numbers-angle/src/main/java/org/apache/commons/numbers/angle/Reduce.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.numbers.angle; + +import java.util.function.DoubleUnaryOperator; + +/** + * Reduces {@code |a - offset|} to the primary interval {@code [0, |period|)}. + * + * Specifically, the {@link #applyAsDouble(double) computed value} is: + * {@code a - |period| * floor((a - offset) / |period|) - offset}. + */ +public class Reduce implements DoubleUnaryOperator { + /** Offset. */ + private final double offset; + /** Period. */ + private final double period; + + /** + * @param offset Value that will be mapped to {@code 0}. + * @param period Period. + */ + public Reduce(double offset, + double period) { + this.offset = offset; + this.period = Math.abs(period); + } + + /** {@inheritDoc} */ + @Override + public double applyAsDouble(double x) { + final double xMo = x - offset; + return xMo - period * Math.floor(xMo / period); + } +} diff --git a/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/ReduceTest.java b/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/ReduceTest.java new file mode 100644 index 0000000..f78818f --- /dev/null +++ b/commons-numbers-angle/src/test/java/org/apache/commons/numbers/angle/ReduceTest.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.numbers.angle; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * Test cases for the {@link Reduce} class. + */ +class ReduceTest { + @Test + void testReduce() { + final double period = 12.222; + final double offset = 13456.789; + + final double delta = 1.5; + + double orig = offset + 122456789 * period + delta; + double expected = delta; + + final Reduce r = new Reduce(offset, period); + Assertions.assertEquals(expected, + r.applyAsDouble(orig), + 1e-7); + + orig = offset - 123356789 * period - delta; + expected = Math.abs(period) - delta; + Assertions.assertEquals(expected, + r.applyAsDouble(orig), + 1e-6); + + orig = offset - 123446789 * period + delta; + expected = delta; + Assertions.assertEquals(expected, + r.applyAsDouble(orig), + 1e-6); + } + + @Test + void testNaN() { + final double[] values = new double[] { + 12.345, -9876.5, Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY + }; + + for (double offset : values) { + for (double period : values) { + for (double x : values) { + final boolean expectedNaN = Double.isNaN(x) || Double.isInfinite(x) || + Double.isNaN(period) || Double.isInfinite(period) || + Double.isNaN(offset) || Double.isInfinite(offset); + + final double v = new Reduce(offset, period).applyAsDouble(x); + if (expectedNaN) { + Assertions.assertTrue(Double.isNaN(v)); + } else { + Assertions.assertFalse(Double.isNaN(v)); + } + } + } + } + } + + @Test + void testReduceNegativePeriod() { + final double period = 12.222; + final double offset = 13; + final double delta = 1.5; + double orig = offset + 122456789 * period + delta; + double expected = delta; + + final Reduce r1 = new Reduce(offset, period); + final Reduce r2 = new Reduce(offset, -period); + Assertions.assertEquals(expected, + r1.applyAsDouble(orig), + 1e-7); + Assertions.assertEquals(r1.applyAsDouble(orig), + r2.applyAsDouble(orig), + 0d); + } + + @Test + void testReduceComparedWithNormalize() { + final double period = 2 * Math.PI; + for (double a = -15; a <= 15; a += 0.5) { + for (double center = -15; center <= 15; center += 1) { + final double nA = PlaneAngleRadians.normalize(a, center); + final double offset = center - Math.PI; + final Reduce reduce = new Reduce(offset, period); + final double r = reduce.applyAsDouble(a) + offset; + Assertions.assertEquals(nA, r, 52 * Math.ulp(nA), + "a=" + a + " center=" + center); + } + } + } +}