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.CalculusFieldElement;
20  import org.hipparchus.special.elliptic.carlson.CarlsonEllipticIntegral;
21  import org.hipparchus.special.elliptic.legendre.LegendreEllipticIntegral;
22  import org.hipparchus.util.FastMath;
23  
24  /** Computation of Jacobi elliptic functions.
25   * The Jacobi elliptic functions are related to elliptic integrals.
26   * @param <T> the type of the field elements
27   * @since 2.0
28   */
29  public abstract class FieldJacobiElliptic<T extends CalculusFieldElement<T>> {
30  
31      /** Parameter of the function. */
32      private final T m;
33  
34      /** Simple constructor.
35       * @param m parameter of the function
36       */
37      protected FieldJacobiElliptic(final T m) {
38          this.m = m;
39      }
40  
41      /** Get the parameter of the function.
42       * @return parameter of the function
43       */
44      public T getM() {
45          return m;
46      }
47  
48      /** Evaluate the three principal Jacobi elliptic functions with pole at point n in Glaisher’s Notation.
49       * @param u argument of the functions
50       * @return copolar trio containing the three principal Jacobi
51       * elliptic functions {@code sn(u|m)}, {@code cn(u|m)}, and {@code dn(u|m)}.
52       */
53      public abstract FieldCopolarN<T> valuesN(T u);
54  
55      /** Evaluate the three principal Jacobi elliptic functions with pole at point n in Glaisher’s Notation.
56       * @param u argument of the functions
57       * @return copolar trio containing the three principal Jacobi
58       * elliptic functions {@code sn(u|m)}, {@code cn(u|m)}, and {@code dn(u|m)}.
59       */
60      public FieldCopolarN<T> valuesN(final double u) {
61          return valuesN(m.newInstance(u));
62      }
63  
64      /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point s in Glaisher’s Notation.
65       * @param u argument of the functions
66       * @return copolar trio containing the three subsidiary Jacobi
67       * elliptic functions {@code cs(u|m)}, {@code ds(u|m)} and {@code ns(u|m)}.
68       */
69      public FieldCopolarS<T> valuesS(final T u) {
70          return new FieldCopolarS<>(valuesN(u));
71      }
72  
73      /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point s in Glaisher’s Notation.
74       * @param u argument of the functions
75       * @return copolar trio containing the three subsidiary Jacobi
76       * elliptic functions {@code cs(u|m)}, {@code ds(u|m)} and {@code ns(u|m)}.
77       */
78      public FieldCopolarS<T> valuesS(final double u) {
79          return new FieldCopolarS<>(valuesN(u));
80      }
81  
82      /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point c in Glaisher’s Notation.
83       * @param u argument of the functions
84       * @return copolar trio containing the three subsidiary Jacobi
85       * elliptic functions {@code dc(u|m)}, {@code nc(u|m)}, and {@code sc(u|m)}.
86       */
87      public FieldCopolarC<T> valuesC(final T u) {
88          return new FieldCopolarC<>(valuesN(u));
89      }
90  
91      /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point c in Glaisher’s Notation.
92       * @param u argument of the functions
93       * @return copolar trio containing the three subsidiary Jacobi
94       * elliptic functions {@code dc(u|m)}, {@code nc(u|m)}, and {@code sc(u|m)}.
95       */
96      public FieldCopolarC<T> valuesC(final double u) {
97          return new FieldCopolarC<>(valuesN(u));
98      }
99  
100     /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point d in Glaisher’s Notation.
101      * @param u argument of the functions
102      * @return copolar trio containing the three subsidiary Jacobi
103      * elliptic functions {@code nd(u|m)}, {@code sd(u|m)}, and {@code cd(u|m)}.
104      */
105     public FieldCopolarD<T> valuesD(final T u) {
106         return new FieldCopolarD<>(valuesN(u));
107     }
108 
109     /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point d in Glaisher’s Notation.
110      * @param u argument of the functions
111      * @return copolar trio containing the three subsidiary Jacobi
112      * elliptic functions {@code nd(u|m)}, {@code sd(u|m)}, and {@code cd(u|m)}.
113      */
114     public FieldCopolarD<T> valuesD(final double u) {
115         return new FieldCopolarD<>(valuesN(u));
116     }
117 
118     /** Evaluate inverse of Jacobi elliptic function sn.
119      * @param x value of Jacobi elliptic function {@code sn(u|m)}
120      * @return u such that {@code x=sn(u|m)}
121      * @since 2.1
122      */
123     public T arcsn(final T x) {
124         // p = n, q = c, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
125         return arcsp(x, x.getField().getOne().negate(), getM().negate());
126     }
127 
128     /** Evaluate inverse of Jacobi elliptic function sn.
129      * @param x value of Jacobi elliptic function {@code sn(u|m)}
130      * @return u such that {@code x=sn(u|m)}
131      * @since 2.1
132      */
133     public T arcsn(final double x) {
134         return arcsn(getM().getField().getZero().newInstance(x));
135     }
136 
137     /** Evaluate inverse of Jacobi elliptic function cn.
138      * @param x value of Jacobi elliptic function {@code cn(u|m)}
139      * @return u such that {@code x=cn(u|m)}
140      * @since 2.1
141      */
142     public T arccn(final T x) {
143         // p = c, q = n, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
144         return arcpqNoDivision(x, x.getField().getOne(), getM().negate());
145     }
146 
147     /** Evaluate inverse of Jacobi elliptic function cn.
148      * @param x value of Jacobi elliptic function {@code cn(u|m)}
149      * @return u such that {@code x=cn(u|m)}
150      * @since 2.1
151      */
152     public T arccn(final double x) {
153         return arccn(getM().getField().getZero().newInstance(x));
154     }
155 
156     /** Evaluate inverse of Jacobi elliptic function dn.
157      * @param x value of Jacobi elliptic function {@code dn(u|m)}
158      * @return u such that {@code x=dn(u|m)}
159      * @since 2.1
160      */
161     public T arcdn(final T x) {
162         // p = d, q = n, r = c, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
163         return arcpqNoDivision(x, getM(), x.getField().getOne().negate());
164     }
165 
166     /** Evaluate inverse of Jacobi elliptic function dn.
167      * @param x value of Jacobi elliptic function {@code dn(u|m)}
168      * @return u such that {@code x=dn(u|m)}
169      * @since 2.1
170      */
171     public T arcdn(final double x) {
172         return arcdn(getM().getField().getZero().newInstance(x));
173     }
174 
175     /** Evaluate inverse of Jacobi elliptic function cs.
176      * @param x value of Jacobi elliptic function {@code cs(u|m)}
177      * @return u such that {@code x=cs(u|m)}
178      * @since 2.1
179      */
180     public T arccs(final T x) {
181         // p = c, q = n, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
182         return arcps(x, x.getField().getOne(), getM().subtract(1).negate());
183     }
184 
185     /** Evaluate inverse of Jacobi elliptic function cs.
186      * @param x value of Jacobi elliptic function {@code cs(u|m)}
187      * @return u such that {@code x=cs(u|m)}
188      * @since 2.1
189      */
190     public T arccs(final double x) {
191         return arccs(getM().getField().getZero().newInstance(x));
192     }
193 
194     /** Evaluate inverse of Jacobi elliptic function ds.
195      * @param x value of Jacobi elliptic function {@code ds(u|m)}
196      * @return u such that {@code x=ds(u|m)}
197      * @since 2.1
198      */
199     public T arcds(final T x) {
200         // p = d, q = c, r = n, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
201         return arcps(x, getM().subtract(1), getM());
202     }
203 
204     /** Evaluate inverse of Jacobi elliptic function ds.
205      * @param x value of Jacobi elliptic function {@code ds(u|m)}
206      * @return u such that {@code x=ds(u|m)}
207      * @since 2.1
208      */
209     public T arcds(final double x) {
210         return arcds(getM().getField().getZero().newInstance(x));
211     }
212 
213     /** Evaluate inverse of Jacobi elliptic function ns.
214      * @param x value of Jacobi elliptic function {@code ns(u|m)}
215      * @return u such that {@code x=ns(u|m)}
216      * @since 2.1
217      */
218     public T arcns(final T x) {
219         // p = n, q = c, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
220         return arcps(x, x.getField().getOne().negate(), getM().negate());
221     }
222 
223     /** Evaluate inverse of Jacobi elliptic function ns.
224      * @param x value of Jacobi elliptic function {@code ns(u|m)}
225      * @return u such that {@code x=ns(u|m)}
226      * @since 2.1
227      */
228     public T arcns(final double x) {
229         return arcns(getM().getField().getZero().newInstance(x));
230     }
231 
232     /** Evaluate inverse of Jacobi elliptic function dc.
233      * @param x value of Jacobi elliptic function {@code dc(u|m)}
234      * @return u such that {@code x=dc(u|m)}
235      * @since 2.1
236      */
237     public T arcdc(final T x) {
238         // p = d, q = c, r = n, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
239         return arcpq(x, getM().subtract(1), x.getField().getOne());
240     }
241 
242     /** Evaluate inverse of Jacobi elliptic function dc.
243      * @param x value of Jacobi elliptic function {@code dc(u|m)}
244      * @return u such that {@code x=dc(u|m)}
245      * @since 2.1
246      */
247     public T arcdc(final double x) {
248         return arcdc(getM().getField().getZero().newInstance(x));
249     }
250 
251     /** Evaluate inverse of Jacobi elliptic function nc.
252      * @param x value of Jacobi elliptic function {@code nc(u|m)}
253      * @return u such that {@code x=nc(u|m)}
254      * @since 2.1
255      */
256     public T arcnc(final T x) {
257         // p = n, q = c, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
258         return arcpq(x, x.getField().getOne().negate(), getM().subtract(1).negate());
259     }
260 
261     /** Evaluate inverse of Jacobi elliptic function nc.
262      * @param x value of Jacobi elliptic function {@code nc(u|m)}
263      * @return u such that {@code x=nc(u|m)}
264      * @since 2.1
265      */
266     public T arcnc(final double x) {
267         return arcnc(getM().getField().getZero().newInstance(x));
268     }
269 
270     /** Evaluate inverse of Jacobi elliptic function sc.
271      * @param x value of Jacobi elliptic function {@code sc(u|m)}
272      * @return u such that {@code x=sc(u|m)}
273      * @since 2.1
274      */
275     public T arcsc(final T x) {
276         // p = c, q = n, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
277         return arcsp(x, x.getField().getOne(), getM().subtract(1).negate());
278     }
279 
280     /** Evaluate inverse of Jacobi elliptic function sc.
281      * @param x value of Jacobi elliptic function {@code sc(u|m)}
282      * @return u such that {@code x=sc(u|m)}
283      * @since 2.1
284      */
285     public T arcsc(final double x) {
286         return arcsc(getM().getField().getZero().newInstance(x));
287     }
288 
289     /** Evaluate inverse of Jacobi elliptic function nd.
290      * @param x value of Jacobi elliptic function {@code nd(u|m)}
291      * @return u such that {@code x=nd(u|m)}
292      * @since 2.1
293      */
294     public T arcnd(final T x) {
295         // p = n, q = d, r = c, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
296         return arcpq(x, getM().negate(), getM().subtract(1));
297     }
298 
299     /** Evaluate inverse of Jacobi elliptic function nd.
300      * @param x value of Jacobi elliptic function {@code nd(u|m)}
301      * @return u such that {@code x=nd(u|m)}
302      * @since 2.1
303      */
304     public T arcnd(final double x) {
305         return arcnd(getM().getField().getZero().newInstance(x));
306     }
307 
308     /** Evaluate inverse of Jacobi elliptic function sd.
309      * @param x value of Jacobi elliptic function {@code sd(u|m)}
310      * @return u such that {@code x=sd(u|m)}
311      * @since 2.1
312      */
313     public T arcsd(final T x) {
314         // p = d, q = n, r = c, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
315         return arcsp(x, getM(), getM().subtract(1));
316     }
317 
318     /** Evaluate inverse of Jacobi elliptic function sd.
319      * @param x value of Jacobi elliptic function {@code sd(u|m)}
320      * @return u such that {@code x=sd(u|m)}
321      * @since 2.1
322      */
323     public T arcsd(final double x) {
324         return arcsd(getM().getField().getZero().newInstance(x));
325     }
326 
327     /** Evaluate inverse of Jacobi elliptic function cd.
328      * @param x value of Jacobi elliptic function {@code cd(u|m)}
329      * @return u such that {@code x=cd(u|m)}
330      * @since 2.1
331      */
332     public T arccd(final T x) {
333         // p = c, q = d, r = n, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
334         return arcpq(x, getM().subtract(1).negate(), getM());
335     }
336 
337     /** Evaluate inverse of Jacobi elliptic function cd.
338      * @param x value of Jacobi elliptic function {@code cd(u|m)}
339      * @return u such that {@code x=cd(u|m)}
340      * @since 2.1
341      */
342     public T arccd(final double x) {
343         return arccd(getM().getField().getZero().newInstance(x));
344     }
345 
346     /** Evaluate inverse of Jacobi elliptic function ps.
347      * <p>
348      * Here p, q, r are any permutation of the letters c, d, n.
349      * </p>
350      * @param x value of Jacobi elliptic function {@code ps(u|m)}
351      * @param deltaQP Δ⁡(q, p) = q⁣s²⁡(u|m) - p⁣s²(u|m) (equation 19.5.28 of DLMF)
352      * @param deltaRP Δ⁡(r, p) = r⁣s²⁡(u|m) - p⁣s²⁡(u|m) (equation 19.5.28 of DLMF)
353      * @return u such that {@code x=ps(u|m)}
354      * @since 2.1
355      */
356     private T arcps(final T x, final T deltaQP, final T deltaRP) {
357         // see equation 19.25.32 in Digital Library of Mathematical Functions
358         // https://dlmf.nist.gov/19.25.E32
359         final T x2       = x.square();
360         final T rf       = CarlsonEllipticIntegral.rF(x2, x2.add(deltaQP), x2.add(deltaRP));
361         return FastMath.copySign(1.0, rf.getReal()) * FastMath.copySign(1.0, x.getReal()) < 0 ?
362                rf.negate() : rf;
363     }
364 
365     /** Evaluate inverse of Jacobi elliptic function sp.
366      * <p>
367      * Here p, q, r are any permutation of the letters c, d, n.
368      * </p>
369      * @param x value of Jacobi elliptic function {@code sp(u|m)}
370      * @param deltaQP Δ⁡(q, p) = q⁣s²⁡(u|m) - p⁣s²(u|m) (equation 19.5.28 of DLMF)
371      * @param deltaRP Δ⁡(r, p) = r⁣s²⁡(u|m) - p⁣s²⁡(u|m) (equation 19.5.28 of DLMF)
372      * @return u such that {@code x=sp(u|m)}
373      * @since 2.1
374      */
375     private T arcsp(final T x, final T deltaQP, final T deltaRP) {
376         // see equation 19.25.33 in Digital Library of Mathematical Functions
377         // https://dlmf.nist.gov/19.25.E33
378         final T x2       = x.square();
379         return x.multiply(CarlsonEllipticIntegral.rF(x.getField().getOne(),
380                                                      deltaQP.multiply(x2).add(1),
381                                                      deltaRP.multiply(x2).add(1)));
382     }
383 
384     /** Evaluate inverse of Jacobi elliptic function pq.
385      * <p>
386      * Here p, q, r are any permutation of the letters c, d, n.
387      * </p>
388      * @param x value of Jacobi elliptic function {@code pq(u|m)}
389      * @param deltaQP Δ⁡(q, p) = q⁣s²⁡(u|m) - p⁣s²(u|m) (equation 19.5.28 of DLMF)
390      * @param deltaRQ Δ⁡(r, q) = r⁣s²⁡(u|m) - q⁣s²⁡(u|m) (equation 19.5.28 of DLMF)
391      * @return u such that {@code x=pq(u|m)}
392      * @since 2.1
393      */
394     private T arcpq(final T x, final T deltaQP, final T deltaRQ) {
395         // see equation 19.25.34 in Digital Library of Mathematical Functions
396         // https://dlmf.nist.gov/19.25.E34
397         final T x2       = x.square();
398         final T w        = x2.subtract(1).negate().divide(deltaQP);
399         final T rf       = CarlsonEllipticIntegral.rF(x2, x.getField().getOne(), deltaRQ.multiply(w).add(1));
400         final T positive = w.sqrt().multiply(rf);
401         return x.getReal() < 0 ? LegendreEllipticIntegral.bigK(getM()).multiply(2).subtract(positive) : positive;
402     }
403 
404     /** Evaluate inverse of Jacobi elliptic function pq.
405      * <p>
406      * Here p, q, r are any permutation of the letters c, d, n.
407      * </p>
408      * <p>
409      * This computed the same thing as {@link #arcpq(CalculusFieldElement, CalculusFieldElement, CalculusFieldElement)}
410      * but uses the homogeneity property Rf(x, y, z) = Rf(ax, ay, az) / √a to get rid of the division
411      * by deltaRQ. This division induces problems in the complex case as it may lose the sign
412      * of zero for values exactly along the real or imaginary axis, hence perturbing branch cuts.
413      * </p>
414      * @param x value of Jacobi elliptic function {@code pq(u|m)}
415      * @param deltaQP Δ⁡(q, p) = q⁣s²⁡(u|m) - p⁣s²(u|m) (equation 19.5.28 of DLMF)
416      * @param deltaRQ Δ⁡(r, q) = r⁣s²⁡(u|m) - q⁣s²⁡(u|m) (equation 19.5.28 of DLMF)
417      * @return u such that {@code x=pq(u|m)}
418      * @since 2.1
419      */
420     private T arcpqNoDivision(final T x, final T deltaQP, final T deltaRQ) {
421         // see equation 19.25.34 in Digital Library of Mathematical Functions
422         // https://dlmf.nist.gov/19.25.E34
423         final T x2       = x.square();
424         final T wDeltaQP = x2.subtract(1).negate();
425         final T rf       = CarlsonEllipticIntegral.rF(x2.multiply(deltaQP), deltaQP, deltaRQ.multiply(wDeltaQP).add(deltaQP));
426         final T positive = wDeltaQP.sqrt().multiply(rf);
427         return FastMath.copySign(1.0, x.getReal()) < 0 ?
428                LegendreEllipticIntegral.bigK(getM()).multiply(2).subtract(positive) :
429                positive;
430      }
431 
432 }