View Javadoc
1   /*
2    * Licensed to the Hipparchus project 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 Hipparchus project 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    *      https://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  package org.hipparchus.special.elliptic.jacobi;
18  
19  import org.hipparchus.analysis.UnivariateFunction;
20  import org.hipparchus.util.FastMath;
21  import org.junit.jupiter.api.Test;
22  
23  import static org.junit.jupiter.api.Assertions.assertEquals;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  class JacobiEllipticTest {
27  
28      @Test
29      void testCircular() {
30          for (double m : new double[] { -1.0e-10, 0.0, 1.0e-10 }) {
31           final double eps = 3 * FastMath.max(1.0e-14, FastMath.abs(m));
32              final JacobiElliptic je = JacobiEllipticBuilder.build(m);
33              for (double t = -10; t < 10; t += 0.01) {
34                  final CopolarN n = je.valuesN(t);
35                  assertEquals(FastMath.sin(t), n.sn(), eps);
36                  assertEquals(FastMath.cos(t), n.cn(), eps);
37                  assertEquals(1.0,             n.dn(), eps);
38              }
39          }
40      }
41  
42      @Test
43      void testHyperbolic() {
44          for (double m1 : new double[] { -1.0e-12, 0.0, 1.0e-12 }) {
45              final double eps = 3 * FastMath.max(1.0e-14, FastMath.abs(m1));
46              final JacobiElliptic je = JacobiEllipticBuilder.build(1.0 - m1);
47              for (double t = -3; t < 3; t += 0.01) {
48                  final CopolarN n = je.valuesN(t);
49                  assertEquals(FastMath.tanh(t),       n.sn(), eps);
50                  assertEquals(1.0 / FastMath.cosh(t), n.cn(), eps);
51                  assertEquals(1.0 / FastMath.cosh(t), n.dn(), eps);
52              }
53          }
54      }
55  
56      @Test
57      void testNoConvergence() {
58          assertTrue(Double.isNaN(JacobiEllipticBuilder.build(Double.NaN).valuesS(0.0).cs()));
59      }
60  
61      @Test
62      void testNegativeParameter() {
63          assertEquals(0.49781366219021166315, JacobiEllipticBuilder.build(-4.5).valuesN(8.3).sn(), 1.5e-10);
64          assertEquals(0.86728401215332559984, JacobiEllipticBuilder.build(-4.5).valuesN(8.3).cn(), 1.5e-10);
65          assertEquals(1.45436686918553524215, JacobiEllipticBuilder.build(-4.5).valuesN(8.3).dn(), 1.5e-10);
66      }
67  
68      @Test
69      void testAbramowitzStegunExample1() {
70          // Abramowitz and Stegun give a result of -1667, but Wolfram Alpha gives the following value
71          assertEquals(-1392.11114434139393839735, JacobiEllipticBuilder.build(0.64).valuesC(1.99650).nc(), 6.0e-10);
72      }
73  
74      @Test
75      void testAbramowitzStegunExample2() {
76          assertEquals(0.996253, JacobiEllipticBuilder.build(0.19).valuesN(0.20).dn(), 1.0e-6);
77      }
78  
79      @Test
80      void testAbramowitzStegunExample3() {
81          assertEquals(0.984056, JacobiEllipticBuilder.build(0.81).valuesN(0.20).dn(), 1.0e-6);
82      }
83  
84      @Test
85      void testAbramowitzStegunExample4() {
86          assertEquals(0.980278, JacobiEllipticBuilder.build(0.81).valuesN(0.20).cn(), 1.0e-6);
87      }
88  
89      @Test
90      void testAbramowitzStegunExample5() {
91          assertEquals(0.60952, JacobiEllipticBuilder.build(0.36).valuesN(0.672).sn(), 1.0e-5);
92          assertEquals(1.1740, JacobiEllipticBuilder.build(0.36).valuesC(0.672).dc(), 1.0e-4);
93      }
94  
95      @Test
96      void testAbramowitzStegunExample7() {
97          assertEquals(1.6918083, JacobiEllipticBuilder.build(0.09).valuesS(0.5360162).cs(), 1.0e-7);
98      }
99  
100     @Test
101     void testAbramowitzStegunExample8() {
102         assertEquals(0.56458, JacobiEllipticBuilder.build(0.5).valuesN(0.61802).sn(), 1.0e-5);
103     }
104 
105     @Test
106     void testAbramowitzStegunExample9() {
107         assertEquals(0.68402, JacobiEllipticBuilder.build(0.5).valuesC(0.61802).sc(), 1.0e-5);
108     }
109 
110     @Test
111     void testAllFunctions() {
112         // reference was computed from Wolfram Alpha, using the square relations
113         // from Abramowitz and Stegun section 16.9 for the functions Wolfram Alpha
114         // did not understood (i.e. for the sake of validation we did *not* use the
115         // relations from section 16.3 which are used in the Hipparchus implementation)
116         final double u = 1.4;
117         final double m = 0.7;
118         final double[] reference = {
119               0.92516138673582827365, 0.37957398289798418747, 0.63312991237590996850,
120               0.41027866958131945027, 0.68434537093007175683, 1.08089249544689572795,
121               1.66800134071905681841, 2.63453251554589286796, 2.43736775548306830513,
122               1.57945467502452678756, 1.46125047743207819361, 0.59951990180590090343
123         };
124         final JacobiElliptic je = JacobiEllipticBuilder.build(m);
125         assertEquals(reference[ 0], je.valuesN(u).sn(), 4 * FastMath.ulp(reference[ 0]));
126         assertEquals(reference[ 1], je.valuesN(u).cn(), 4 * FastMath.ulp(reference[ 1]));
127         assertEquals(reference[ 2], je.valuesN(u).dn(), 4 * FastMath.ulp(reference[ 2]));
128         assertEquals(reference[ 3], je.valuesS(u).cs(), 4 * FastMath.ulp(reference[ 3]));
129         assertEquals(reference[ 4], je.valuesS(u).ds(), 4 * FastMath.ulp(reference[ 4]));
130         assertEquals(reference[ 5], je.valuesS(u).ns(), 4 * FastMath.ulp(reference[ 5]));
131         assertEquals(reference[ 6], je.valuesC(u).dc(), 4 * FastMath.ulp(reference[ 6]));
132         assertEquals(reference[ 7], je.valuesC(u).nc(), 4 * FastMath.ulp(reference[ 7]));
133         assertEquals(reference[ 8], je.valuesC(u).sc(), 4 * FastMath.ulp(reference[ 8]));
134         assertEquals(reference[ 9], je.valuesD(u).nd(), 4 * FastMath.ulp(reference[ 9]));
135         assertEquals(reference[10], je.valuesD(u).sd(), 4 * FastMath.ulp(reference[10]));
136         assertEquals(reference[11], je.valuesD(u).cd(), 4 * FastMath.ulp(reference[11]));
137     }
138 
139     @Test
140     void testInverseCopolarN() {
141         final double m = 0.7;
142         final JacobiElliptic je = JacobiEllipticBuilder.build(m);
143         doTestInverse(-0.80,  0.80, 100, u -> je.valuesN(u).sn(), x -> je.arcsn(x), 1.0e-14);
144         doTestInverse(-1.00,  1.00, 100, u -> je.valuesN(u).cn(), x -> je.arccn(x), 1.0e-14);
145         doTestInverse( 0.55,  1.00, 100, u -> je.valuesN(u).dn(), x -> je.arcdn(x), 1.0e-14);
146     }
147 
148     @Test
149     void testInverseCopolarS() {
150         final double m = 0.7;
151         final JacobiElliptic je = JacobiEllipticBuilder.build(m);
152         doTestInverse(-2.00,  2.00, 100, u -> je.valuesS(u).cs(), x -> je.arccs(x), 1.0e-14);
153         doTestInverse( 0.55,  2.00, 100, u -> je.valuesS(u).ds(), x -> je.arcds(x), 1.0e-14);
154         doTestInverse(-2.00, -0.55, 100, u -> je.valuesS(u).ds(), x -> je.arcds(x), 1.0e-14);
155         doTestInverse( 1.00,  2.00, 100, u -> je.valuesS(u).ns(), x -> je.arcns(x), 1.0e-11);
156         doTestInverse(-2.00, -1.00, 100, u -> je.valuesS(u).ns(), x -> je.arcns(x), 1.0e-11);
157     }
158 
159     @Test
160     void testInverseCopolarC() {
161         final double m = 0.7;
162         final JacobiElliptic je = JacobiEllipticBuilder.build(m);
163         doTestInverse( 1.00,  2.00, 100, u -> je.valuesC(u).dc(), x -> je.arcdc(x), 1.0e-14);
164         doTestInverse(-2.00, -1.00, 100, u -> je.valuesC(u).dc(), x -> je.arcdc(x), 1.0e-14);
165         doTestInverse( 1.00,  2.00, 100, u -> je.valuesC(u).nc(), x -> je.arcnc(x), 1.0e-14);
166         doTestInverse(-2.00, -1.00, 100, u -> je.valuesC(u).nc(), x -> je.arcnc(x), 1.0e-14);
167         doTestInverse(-2.00,  2.00, 100, u -> je.valuesC(u).sc(), x -> je.arcsc(x), 1.0e-14);
168     }
169 
170     @Test
171     void testInverseCopolarD() {
172         final double m = 0.7;
173         final JacobiElliptic je = JacobiEllipticBuilder.build(m);
174         doTestInverse( 1.00,  1.80, 100, u -> je.valuesD(u).nd(), x -> je.arcnd(x), 1.0e-14);
175         doTestInverse(-1.80,  1.80, 100, u -> je.valuesD(u).sd(), x -> je.arcsd(x), 1.0e-14);
176         doTestInverse(-1.00,  1.00, 100, u -> je.valuesD(u).cd(), x -> je.arccd(x), 1.0e-14);
177     }
178 
179     private void doTestInverse(final double xMin, final double xMax, final int n,
180                                final UnivariateFunction direct, final UnivariateFunction inverse,
181                                final double tolerance) {
182         for (int i = 0; i < n; ++i) {
183             final double x        = xMin + i * (xMax - xMin) / (n - 1);
184             final double xRebuilt = direct.value(inverse.value(x));
185             assertEquals(x, xRebuilt, tolerance);
186         }
187     }
188 
189 }