1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.commons.numbers.complex; 19 20 import org.apache.commons.numbers.complex.TestUtils.TestDataFlagOption; 21 import org.junit.jupiter.api.Assertions; 22 import org.junit.jupiter.api.Test; 23 24 import java.util.List; 25 import java.util.function.BiFunction; 26 import java.util.function.Supplier; 27 import java.util.function.UnaryOperator; 28 29 /** 30 * Tests the functions defined by the C.99 standard for complex numbers 31 * defined in ISO/IEC 9899, Annex G. 32 * 33 * <p>The test data is generated from a known implementation of the standard 34 * and saved to the test resources data files. 35 * 36 * @see <a href="http://www.open-std.org/JTC1/SC22/WG14/www/standards"> 37 * ISO/IEC 9899 - Programming languages - C</a> 38 */ 39 class CReferenceTest { 40 /** Positive zero bits. */ 41 private static final long POSITIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(+0.0); 42 /** Negative zero bits. */ 43 private static final long NEGATIVE_ZERO_DOUBLE_BITS = Double.doubleToRawLongBits(-0.0); 44 45 /** 46 * The maximum units of least precision (ULPs) allowed between values. 47 * This is a global setting used to override individual test settings for ULPs as follows: 48 * 49 * <ul> 50 * <li>In the normal use case this is set to zero and ignored. 51 * <li>If the sign matches the setting of the test then the larger magnitude is used. 52 * <li>If the global setting is negative and the test setting is positive then it overrides 53 * the individual test setting for reporting purposes. 54 * <li>If the global setting is positive and the test setting is negative then the test 55 * setting takes precedence. 56 * </ul> 57 * 58 * <p>During testing the difference between an expected and actual result is specified by the 59 * count of numbers in the range between them lower end exclusive and upper end inclusive. 60 * Two equal numbers have a count of 0. The next adjacent value has a count of 1. 61 * 62 * <p>If the configured ULPs is positive then the test is 63 * asserted using: {@code delta <= maxUlps}. 64 * 65 * <p>If the configured ULPs is negative the test is asserted using: 66 * {@code delta <= (|maxUlps|-1)}. This allows setting a ULPs of -1 to indicate 67 * maximum ULPs = 0 but flagging the assertion for special processing. 68 * 69 * <p>If the maximum ULPs is positive then an assertion error is raised. 70 * If negative then the error is printed to System out. This allows reporting of large 71 * deviations between the library and the reference data. 72 * 73 * <p>In a standard use-case all tests will have a configured positive maximum ULPs to 74 * pass the current test data. The global setting can be set to a negative value to allow 75 * reporting of errors larger in magnitude to the console. Setting -1 will output all 76 * differences. Setting -2 will output only those with a value between the two numbers, 77 * i.e. the numbers are not the same to floating point roundoff. 78 * 79 * <p>Setting the global maximum ULPs to negative has the second effect of loading all 80 * data that has been flagged in data files using the {@code ;} character. 81 * Otherwise this data is ignored by testing and printed to System out. 82 */ 83 private static long globalMaxUlps = 0; 84 85 /** Set this to true to report all deviations to System out when the maximum ULPs is negative. */ 86 private static boolean reportAllDeviations = false; 87 88 /** 89 * Assert the two numbers are equal within the provided units of least precision. 90 * The maximum count of numbers allowed between the two values is {@code maxUlps - 1}. 91 * 92 * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal. 93 * 94 * @param msg the failure message 95 * @param expected the expected 96 * @param actual the actual 97 * @param maxUlps the maximum units of least precision between the two values 98 */ 99 static void assertEquals(Supplier<String> msg, double expected, double actual, long maxUlps) { 100 final long e = Double.doubleToLongBits(expected); 101 final long a = Double.doubleToLongBits(actual); 102 103 // Code adapted from Precision#equals(double, double, int) so we maintain the delta 104 // for the message. 105 106 long delta; 107 boolean equal; 108 if (e == a) { 109 // Binary equal 110 equal = true; 111 delta = 0; 112 } else if ((a ^ e) < 0L) { 113 // Opposite signs are never equal. 114 equal = false; 115 // The difference is the count of numbers between each and zero. 116 // This may overflow but we report it using an unsigned formatter. 117 if (a < e) { 118 delta = (e - POSITIVE_ZERO_DOUBLE_BITS) + (a - NEGATIVE_ZERO_DOUBLE_BITS) + 1; 119 } else { 120 delta = (a - POSITIVE_ZERO_DOUBLE_BITS) + (e - NEGATIVE_ZERO_DOUBLE_BITS) + 1; 121 } 122 } else { 123 delta = Math.abs(e - a); 124 // Allow input of a negative maximum ULPs 125 equal = delta <= ((maxUlps < 0) ? (-maxUlps - 1) : maxUlps); 126 } 127 128 // DEBUG: 129 if (maxUlps < 0) { 130 // CHECKSTYLE: stop Regex 131 if (!equal) { 132 System.out.printf("%s: expected <%s> != actual <%s> (ulps=%s)%n", 133 msg.get(), expected, actual, Long.toUnsignedString(delta)); 134 } else if (reportAllDeviations) { 135 System.out.printf("%s: expected <%s> == actual <%s> (ulps=0)%n", 136 msg.get(), expected, actual); 137 } 138 // CHECKSTYLE: resume Regex 139 return; 140 } 141 142 if (!equal) { 143 Assertions.fail(String.format("%s: expected <%s> != actual <%s> (ulps=%s)", 144 msg.get(), expected, actual, Long.toUnsignedString(delta))); 145 } 146 } 147 148 /** 149 * Assert the operation on the complex number is equal to the expected value. 150 * 151 * <p>The results are considered equal within the provided units of least 152 * precision. The maximum count of numbers allowed between the two values is 153 * {@code maxUlps - 1}. 154 * 155 * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal. 156 * 157 * @param c Input number. 158 * @param name the operation name 159 * @param operation the operation 160 * @param expected Expected result. 161 * @param maxUlps the maximum units of least precision between the two values 162 */ 163 static void assertComplex(Complex c, 164 String name, UnaryOperator<Complex> operation, 165 Complex expected, long maxUlps) { 166 final Complex z = operation.apply(c); 167 assertEquals(() -> c + "." + name + "(): real", expected.real(), z.real(), maxUlps); 168 assertEquals(() -> c + "." + name + "(): imaginary", expected.imag(), z.imag(), maxUlps); 169 } 170 171 /** 172 * Assert the operation on the complex numbers is equal to the expected value. 173 * 174 * <p>The results are considered equal within the provided units of least 175 * precision. The maximum count of numbers allowed between the two values is 176 * {@code maxUlps - 1}. 177 * 178 * <p>Numbers must have the same sign. Thus -0.0 and 0.0 are never equal. 179 * 180 * @param c1 First number. 181 * @param c2 Second number. 182 * @param name the operation name 183 * @param operation the operation 184 * @param expected Expected real part. 185 * @param maxUlps the maximum units of least precision between the two values 186 */ 187 static void assertComplex(Complex c1, Complex c2, 188 String name, BiFunction<Complex, Complex, Complex> operation, 189 Complex expected, long maxUlps) { 190 final Complex z = operation.apply(c1, c2); 191 assertEquals(() -> c1 + "." + name + c2 + ": real", expected.real(), z.real(), maxUlps); 192 assertEquals(() -> c1 + "." + name + c2 + ": imaginary", expected.imag(), z.imag(), maxUlps); 193 } 194 195 /** 196 * Assert the operation using the data loaded from test resources. 197 * 198 * @param name the operation name 199 * @param operation the operation 200 * @param maxUlps the maximum units of least precision between the two values 201 */ 202 private static void assertOperation(String name, 203 UnaryOperator<Complex> operation, long maxUlps) { 204 final List<Complex[]> data = loadTestData(name); 205 final long ulps = getTestUlps(maxUlps); 206 for (final Complex[] pair : data) { 207 assertComplex(pair[0], name, operation, pair[1], ulps); 208 } 209 } 210 211 /** 212 * Assert the operation using the data loaded from test resources. 213 * 214 * @param name the operation name 215 * @param operation the operation 216 * @param maxUlps the maximum units of least precision between the two values 217 */ 218 private static void assertBiOperation(String name, 219 BiFunction<Complex, Complex, Complex> operation, long maxUlps) { 220 final List<Complex[]> data = loadTestData(name); 221 final long ulps = getTestUlps(maxUlps); 222 for (final Complex[] triple : data) { 223 assertComplex(triple[0], triple[1], name, operation, triple[2], ulps); 224 } 225 } 226 227 /** 228 * Assert the operation using the data loaded from test resources. 229 * 230 * @param testData Test data resource name. 231 * @return the list 232 */ 233 private static List<Complex[]> loadTestData(String name) { 234 final String testData = "data/" + name + ".txt"; 235 final TestDataFlagOption option = globalMaxUlps < 1 ? 236 TestDataFlagOption.LOAD : TestDataFlagOption.IGNORE; 237 return TestUtils.loadTestData(testData, option, 238 // CHECKSTYLE: stop Regex 239 s -> System.out.println(name + " IGNORED: " + s)); 240 // CHECKSTYLE: resume Regex 241 } 242 243 /** 244 * Gets the test ulps. This uses the input value of the global setting if that is greater 245 * in magnitude. 246 * 247 * @param ulps the ulps 248 * @return the test ulps 249 */ 250 private static long getTestUlps(long ulps) { 251 // If sign matches use the larger magnitude. 252 // xor the sign bytes will be negative if the sign does not match 253 if ((globalMaxUlps ^ ulps) >= 0) { 254 final long max = Math.max(Math.abs(globalMaxUlps), Math.abs(ulps)); 255 // restore sign 256 return ulps < 0 ? -max : max; 257 } 258 // If the global setting is negative and the test setting is positive then it overrides 259 // the individual test setting for reporting purposes. 260 if (globalMaxUlps < 0) { 261 return globalMaxUlps; 262 } 263 // If the global setting is positive and the test setting is negative then the test 264 // setting takes precedence. 265 return ulps; 266 } 267 268 @Test 269 void testAcos() { 270 assertOperation("acos", Complex::acos, 2); 271 } 272 273 @Test 274 void testAcosh() { 275 assertOperation("acosh", Complex::acosh, 2); 276 } 277 278 @Test 279 void testAsinh() { 280 // Odd function: negative real cases defined by positive real cases 281 assertOperation("asinh", Complex::asinh, 3); 282 } 283 284 @Test 285 void testAtanh() { 286 // Odd function: negative real cases defined by positive real cases 287 assertOperation("atanh", Complex::atanh, 1); 288 } 289 290 @Test 291 void testCosh() { 292 // Even function: negative real cases defined by positive real cases 293 assertOperation("cosh", Complex::cosh, 2); 294 } 295 296 @Test 297 void testSinh() { 298 // Odd function: negative real cases defined by positive real cases 299 assertOperation("sinh", Complex::sinh, 2); 300 } 301 302 @Test 303 void testTanh() { 304 // Odd function: negative real cases defined by positive real cases 305 assertOperation("tanh", Complex::tanh, 2); 306 } 307 308 @Test 309 void testExp() { 310 assertOperation("exp", Complex::exp, 2); 311 } 312 313 @Test 314 void testLog() { 315 assertOperation("log", Complex::log, 1); 316 } 317 318 @Test 319 void testSqrt() { 320 assertOperation("sqrt", Complex::sqrt, 1); 321 } 322 323 @Test 324 void testMultiply() { 325 assertBiOperation("multiply", Complex::multiply, 0); 326 } 327 328 @Test 329 void testDivide() { 330 assertBiOperation("divide", Complex::divide, 7); 331 } 332 333 @Test 334 void testPowComplex() { 335 assertBiOperation("pow", Complex::pow, 9); 336 } 337 }