FieldJacobiElliptic.java

  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. import org.hipparchus.CalculusFieldElement;
  19. import org.hipparchus.special.elliptic.carlson.CarlsonEllipticIntegral;
  20. import org.hipparchus.special.elliptic.legendre.LegendreEllipticIntegral;
  21. import org.hipparchus.util.FastMath;

  22. /** Computation of Jacobi elliptic functions.
  23.  * The Jacobi elliptic functions are related to elliptic integrals.
  24.  * @param <T> the type of the field elements
  25.  * @since 2.0
  26.  */
  27. public abstract class FieldJacobiElliptic<T extends CalculusFieldElement<T>> {

  28.     /** Parameter of the function. */
  29.     private final T m;

  30.     /** Simple constructor.
  31.      * @param m parameter of the function
  32.      */
  33.     protected FieldJacobiElliptic(final T m) {
  34.         this.m = m;
  35.     }

  36.     /** Get the parameter of the function.
  37.      * @return parameter of the function
  38.      */
  39.     public T getM() {
  40.         return m;
  41.     }

  42.     /** Evaluate the three principal Jacobi elliptic functions with pole at point n in Glaisher’s Notation.
  43.      * @param u argument of the functions
  44.      * @return copolar trio containing the three principal Jacobi
  45.      * elliptic functions {@code sn(u|m)}, {@code cn(u|m)}, and {@code dn(u|m)}.
  46.      */
  47.     public abstract FieldCopolarN<T> valuesN(T u);

  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 FieldCopolarN<T> valuesN(final double u) {
  54.         return valuesN(m.newInstance(u));
  55.     }

  56.     /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point s in Glaisher’s Notation.
  57.      * @param u argument of the functions
  58.      * @return copolar trio containing the three subsidiary Jacobi
  59.      * elliptic functions {@code cs(u|m)}, {@code ds(u|m)} and {@code ns(u|m)}.
  60.      */
  61.     public FieldCopolarS<T> valuesS(final T u) {
  62.         return new FieldCopolarS<>(valuesN(u));
  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 double u) {
  70.         return new FieldCopolarS<>(valuesN(u));
  71.     }

  72.     /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point c in Glaisher’s Notation.
  73.      * @param u argument of the functions
  74.      * @return copolar trio containing the three subsidiary Jacobi
  75.      * elliptic functions {@code dc(u|m)}, {@code nc(u|m)}, and {@code sc(u|m)}.
  76.      */
  77.     public FieldCopolarC<T> valuesC(final T u) {
  78.         return new FieldCopolarC<>(valuesN(u));
  79.     }

  80.     /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point c in Glaisher’s Notation.
  81.      * @param u argument of the functions
  82.      * @return copolar trio containing the three subsidiary Jacobi
  83.      * elliptic functions {@code dc(u|m)}, {@code nc(u|m)}, and {@code sc(u|m)}.
  84.      */
  85.     public FieldCopolarC<T> valuesC(final double u) {
  86.         return new FieldCopolarC<>(valuesN(u));
  87.     }

  88.     /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point d in Glaisher’s Notation.
  89.      * @param u argument of the functions
  90.      * @return copolar trio containing the three subsidiary Jacobi
  91.      * elliptic functions {@code nd(u|m)}, {@code sd(u|m)}, and {@code cd(u|m)}.
  92.      */
  93.     public FieldCopolarD<T> valuesD(final T u) {
  94.         return new FieldCopolarD<>(valuesN(u));
  95.     }

  96.     /** Evaluate the three subsidiary Jacobi elliptic functions with pole at point d in Glaisher’s Notation.
  97.      * @param u argument of the functions
  98.      * @return copolar trio containing the three subsidiary Jacobi
  99.      * elliptic functions {@code nd(u|m)}, {@code sd(u|m)}, and {@code cd(u|m)}.
  100.      */
  101.     public FieldCopolarD<T> valuesD(final double u) {
  102.         return new FieldCopolarD<>(valuesN(u));
  103.     }

  104.     /** Evaluate inverse of Jacobi elliptic function sn.
  105.      * @param x value of Jacobi elliptic function {@code sn(u|m)}
  106.      * @return u such that {@code x=sn(u|m)}
  107.      * @since 2.1
  108.      */
  109.     public T arcsn(final T x) {
  110.         // p = n, q = c, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
  111.         return arcsp(x, x.getField().getOne().negate(), getM().negate());
  112.     }

  113.     /** Evaluate inverse of Jacobi elliptic function sn.
  114.      * @param x value of Jacobi elliptic function {@code sn(u|m)}
  115.      * @return u such that {@code x=sn(u|m)}
  116.      * @since 2.1
  117.      */
  118.     public T arcsn(final double x) {
  119.         return arcsn(getM().getField().getZero().newInstance(x));
  120.     }

  121.     /** Evaluate inverse of Jacobi elliptic function cn.
  122.      * @param x value of Jacobi elliptic function {@code cn(u|m)}
  123.      * @return u such that {@code x=cn(u|m)}
  124.      * @since 2.1
  125.      */
  126.     public T arccn(final T x) {
  127.         // p = c, q = n, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
  128.         return arcpqNoDivision(x, x.getField().getOne(), getM().negate());
  129.     }

  130.     /** Evaluate inverse of Jacobi elliptic function cn.
  131.      * @param x value of Jacobi elliptic function {@code cn(u|m)}
  132.      * @return u such that {@code x=cn(u|m)}
  133.      * @since 2.1
  134.      */
  135.     public T arccn(final double x) {
  136.         return arccn(getM().getField().getZero().newInstance(x));
  137.     }

  138.     /** Evaluate inverse of Jacobi elliptic function dn.
  139.      * @param x value of Jacobi elliptic function {@code dn(u|m)}
  140.      * @return u such that {@code x=dn(u|m)}
  141.      * @since 2.1
  142.      */
  143.     public T arcdn(final T x) {
  144.         // p = d, q = n, r = c, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
  145.         return arcpqNoDivision(x, getM(), x.getField().getOne().negate());
  146.     }

  147.     /** Evaluate inverse of Jacobi elliptic function dn.
  148.      * @param x value of Jacobi elliptic function {@code dn(u|m)}
  149.      * @return u such that {@code x=dn(u|m)}
  150.      * @since 2.1
  151.      */
  152.     public T arcdn(final double x) {
  153.         return arcdn(getM().getField().getZero().newInstance(x));
  154.     }

  155.     /** Evaluate inverse of Jacobi elliptic function cs.
  156.      * @param x value of Jacobi elliptic function {@code cs(u|m)}
  157.      * @return u such that {@code x=cs(u|m)}
  158.      * @since 2.1
  159.      */
  160.     public T arccs(final T x) {
  161.         // p = c, q = n, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
  162.         return arcps(x, x.getField().getOne(), getM().subtract(1).negate());
  163.     }

  164.     /** Evaluate inverse of Jacobi elliptic function cs.
  165.      * @param x value of Jacobi elliptic function {@code cs(u|m)}
  166.      * @return u such that {@code x=cs(u|m)}
  167.      * @since 2.1
  168.      */
  169.     public T arccs(final double x) {
  170.         return arccs(getM().getField().getZero().newInstance(x));
  171.     }

  172.     /** Evaluate inverse of Jacobi elliptic function ds.
  173.      * @param x value of Jacobi elliptic function {@code ds(u|m)}
  174.      * @return u such that {@code x=ds(u|m)}
  175.      * @since 2.1
  176.      */
  177.     public T arcds(final T x) {
  178.         // p = d, q = c, r = n, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
  179.         return arcps(x, getM().subtract(1), getM());
  180.     }

  181.     /** Evaluate inverse of Jacobi elliptic function ds.
  182.      * @param x value of Jacobi elliptic function {@code ds(u|m)}
  183.      * @return u such that {@code x=ds(u|m)}
  184.      * @since 2.1
  185.      */
  186.     public T arcds(final double x) {
  187.         return arcds(getM().getField().getZero().newInstance(x));
  188.     }

  189.     /** Evaluate inverse of Jacobi elliptic function ns.
  190.      * @param x value of Jacobi elliptic function {@code ns(u|m)}
  191.      * @return u such that {@code x=ns(u|m)}
  192.      * @since 2.1
  193.      */
  194.     public T arcns(final T x) {
  195.         // p = n, q = c, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
  196.         return arcps(x, x.getField().getOne().negate(), getM().negate());
  197.     }

  198.     /** Evaluate inverse of Jacobi elliptic function ns.
  199.      * @param x value of Jacobi elliptic function {@code ns(u|m)}
  200.      * @return u such that {@code x=ns(u|m)}
  201.      * @since 2.1
  202.      */
  203.     public T arcns(final double x) {
  204.         return arcns(getM().getField().getZero().newInstance(x));
  205.     }

  206.     /** Evaluate inverse of Jacobi elliptic function dc.
  207.      * @param x value of Jacobi elliptic function {@code dc(u|m)}
  208.      * @return u such that {@code x=dc(u|m)}
  209.      * @since 2.1
  210.      */
  211.     public T arcdc(final T x) {
  212.         // p = d, q = c, r = n, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
  213.         return arcpq(x, getM().subtract(1), x.getField().getOne());
  214.     }

  215.     /** Evaluate inverse of Jacobi elliptic function dc.
  216.      * @param x value of Jacobi elliptic function {@code dc(u|m)}
  217.      * @return u such that {@code x=dc(u|m)}
  218.      * @since 2.1
  219.      */
  220.     public T arcdc(final double x) {
  221.         return arcdc(getM().getField().getZero().newInstance(x));
  222.     }

  223.     /** Evaluate inverse of Jacobi elliptic function nc.
  224.      * @param x value of Jacobi elliptic function {@code nc(u|m)}
  225.      * @return u such that {@code x=nc(u|m)}
  226.      * @since 2.1
  227.      */
  228.     public T arcnc(final T x) {
  229.         // p = n, q = c, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
  230.         return arcpq(x, x.getField().getOne().negate(), getM().subtract(1).negate());
  231.     }

  232.     /** Evaluate inverse of Jacobi elliptic function nc.
  233.      * @param x value of Jacobi elliptic function {@code nc(u|m)}
  234.      * @return u such that {@code x=nc(u|m)}
  235.      * @since 2.1
  236.      */
  237.     public T arcnc(final double x) {
  238.         return arcnc(getM().getField().getZero().newInstance(x));
  239.     }

  240.     /** Evaluate inverse of Jacobi elliptic function sc.
  241.      * @param x value of Jacobi elliptic function {@code sc(u|m)}
  242.      * @return u such that {@code x=sc(u|m)}
  243.      * @since 2.1
  244.      */
  245.     public T arcsc(final T x) {
  246.         // p = c, q = n, r = d, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
  247.         return arcsp(x, x.getField().getOne(), getM().subtract(1).negate());
  248.     }

  249.     /** Evaluate inverse of Jacobi elliptic function sc.
  250.      * @param x value of Jacobi elliptic function {@code sc(u|m)}
  251.      * @return u such that {@code x=sc(u|m)}
  252.      * @since 2.1
  253.      */
  254.     public T arcsc(final double x) {
  255.         return arcsc(getM().getField().getZero().newInstance(x));
  256.     }

  257.     /** Evaluate inverse of Jacobi elliptic function nd.
  258.      * @param x value of Jacobi elliptic function {@code nd(u|m)}
  259.      * @return u such that {@code x=nd(u|m)}
  260.      * @since 2.1
  261.      */
  262.     public T arcnd(final T x) {
  263.         // p = n, q = d, r = c, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
  264.         return arcpq(x, getM().negate(), getM().subtract(1));
  265.     }

  266.     /** Evaluate inverse of Jacobi elliptic function nd.
  267.      * @param x value of Jacobi elliptic function {@code nd(u|m)}
  268.      * @return u such that {@code x=nd(u|m)}
  269.      * @since 2.1
  270.      */
  271.     public T arcnd(final double x) {
  272.         return arcnd(getM().getField().getZero().newInstance(x));
  273.     }

  274.     /** Evaluate inverse of Jacobi elliptic function sd.
  275.      * @param x value of Jacobi elliptic function {@code sd(u|m)}
  276.      * @return u such that {@code x=sd(u|m)}
  277.      * @since 2.1
  278.      */
  279.     public T arcsd(final T x) {
  280.         // p = d, q = n, r = c, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, p)
  281.         return arcsp(x, getM(), getM().subtract(1));
  282.     }

  283.     /** Evaluate inverse of Jacobi elliptic function sd.
  284.      * @param x value of Jacobi elliptic function {@code sd(u|m)}
  285.      * @return u such that {@code x=sd(u|m)}
  286.      * @since 2.1
  287.      */
  288.     public T arcsd(final double x) {
  289.         return arcsd(getM().getField().getZero().newInstance(x));
  290.     }

  291.     /** Evaluate inverse of Jacobi elliptic function cd.
  292.      * @param x value of Jacobi elliptic function {@code cd(u|m)}
  293.      * @return u such that {@code x=cd(u|m)}
  294.      * @since 2.1
  295.      */
  296.     public T arccd(final T x) {
  297.         // p = c, q = d, r = n, see DLMF 19.25.29 for evaluating Δ⁡(q, p) and Δ⁡(r, q)
  298.         return arcpq(x, getM().subtract(1).negate(), getM());
  299.     }

  300.     /** Evaluate inverse of Jacobi elliptic function cd.
  301.      * @param x value of Jacobi elliptic function {@code cd(u|m)}
  302.      * @return u such that {@code x=cd(u|m)}
  303.      * @since 2.1
  304.      */
  305.     public T arccd(final double x) {
  306.         return arccd(getM().getField().getZero().newInstance(x));
  307.     }

  308.     /** Evaluate inverse of Jacobi elliptic function ps.
  309.      * <p>
  310.      * Here p, q, r are any permutation of the letters c, d, n.
  311.      * </p>
  312.      * @param x value of Jacobi elliptic function {@code ps(u|m)}
  313.      * @param deltaQP Δ⁡(q, p) = q⁣s²⁡(u|m) - p⁣s²(u|m) (equation 19.5.28 of DLMF)
  314.      * @param deltaRP Δ⁡(r, p) = r⁣s²⁡(u|m) - p⁣s²⁡(u|m) (equation 19.5.28 of DLMF)
  315.      * @return u such that {@code x=ps(u|m)}
  316.      * @since 2.1
  317.      */
  318.     private T arcps(final T x, final T deltaQP, final T deltaRP) {
  319.         // see equation 19.25.32 in Digital Library of Mathematical Functions
  320.         // https://dlmf.nist.gov/19.25.E32
  321.         final T x2       = x.square();
  322.         final T rf       = CarlsonEllipticIntegral.rF(x2, x2.add(deltaQP), x2.add(deltaRP));
  323.         return FastMath.copySign(1.0, rf.getReal()) * FastMath.copySign(1.0, x.getReal()) < 0 ?
  324.                rf.negate() : rf;
  325.     }

  326.     /** Evaluate inverse of Jacobi elliptic function sp.
  327.      * <p>
  328.      * Here p, q, r are any permutation of the letters c, d, n.
  329.      * </p>
  330.      * @param x value of Jacobi elliptic function {@code sp(u|m)}
  331.      * @param deltaQP Δ⁡(q, p) = q⁣s²⁡(u|m) - p⁣s²(u|m) (equation 19.5.28 of DLMF)
  332.      * @param deltaRP Δ⁡(r, p) = r⁣s²⁡(u|m) - p⁣s²⁡(u|m) (equation 19.5.28 of DLMF)
  333.      * @return u such that {@code x=sp(u|m)}
  334.      * @since 2.1
  335.      */
  336.     private T arcsp(final T x, final T deltaQP, final T deltaRP) {
  337.         // see equation 19.25.33 in Digital Library of Mathematical Functions
  338.         // https://dlmf.nist.gov/19.25.E33
  339.         final T x2       = x.square();
  340.         return x.multiply(CarlsonEllipticIntegral.rF(x.getField().getOne(),
  341.                                                      deltaQP.multiply(x2).add(1),
  342.                                                      deltaRP.multiply(x2).add(1)));
  343.     }

  344.     /** Evaluate inverse of Jacobi elliptic function pq.
  345.      * <p>
  346.      * Here p, q, r are any permutation of the letters c, d, n.
  347.      * </p>
  348.      * @param x value of Jacobi elliptic function {@code pq(u|m)}
  349.      * @param deltaQP Δ⁡(q, p) = q⁣s²⁡(u|m) - p⁣s²(u|m) (equation 19.5.28 of DLMF)
  350.      * @param deltaRQ Δ⁡(r, q) = r⁣s²⁡(u|m) - q⁣s²⁡(u|m) (equation 19.5.28 of DLMF)
  351.      * @return u such that {@code x=pq(u|m)}
  352.      * @since 2.1
  353.      */
  354.     private T arcpq(final T x, final T deltaQP, final T deltaRQ) {
  355.         // see equation 19.25.34 in Digital Library of Mathematical Functions
  356.         // https://dlmf.nist.gov/19.25.E34
  357.         final T x2       = x.square();
  358.         final T w        = x2.subtract(1).negate().divide(deltaQP);
  359.         final T rf       = CarlsonEllipticIntegral.rF(x2, x.getField().getOne(), deltaRQ.multiply(w).add(1));
  360.         final T positive = w.sqrt().multiply(rf);
  361.         return x.getReal() < 0 ? LegendreEllipticIntegral.bigK(getM()).multiply(2).subtract(positive) : positive;
  362.     }

  363.     /** Evaluate inverse of Jacobi elliptic function pq.
  364.      * <p>
  365.      * Here p, q, r are any permutation of the letters c, d, n.
  366.      * </p>
  367.      * <p>
  368.      * This computed the same thing as {@link #arcpq(CalculusFieldElement, CalculusFieldElement, CalculusFieldElement)}
  369.      * but uses the homogeneity property Rf(x, y, z) = Rf(ax, ay, az) / √a to get rid of the division
  370.      * by deltaRQ. This division induces problems in the complex case as it may lose the sign
  371.      * of zero for values exactly along the real or imaginary axis, hence perturbing branch cuts.
  372.      * </p>
  373.      * @param x value of Jacobi elliptic function {@code pq(u|m)}
  374.      * @param deltaQP Δ⁡(q, p) = q⁣s²⁡(u|m) - p⁣s²(u|m) (equation 19.5.28 of DLMF)
  375.      * @param deltaRQ Δ⁡(r, q) = r⁣s²⁡(u|m) - q⁣s²⁡(u|m) (equation 19.5.28 of DLMF)
  376.      * @return u such that {@code x=pq(u|m)}
  377.      * @since 2.1
  378.      */
  379.     private T arcpqNoDivision(final T x, final T deltaQP, final T deltaRQ) {
  380.         // see equation 19.25.34 in Digital Library of Mathematical Functions
  381.         // https://dlmf.nist.gov/19.25.E34
  382.         final T x2       = x.square();
  383.         final T wDeltaQP = x2.subtract(1).negate();
  384.         final T rf       = CarlsonEllipticIntegral.rF(x2.multiply(deltaQP), deltaQP, deltaRQ.multiply(wDeltaQP).add(deltaQP));
  385.         final T positive = wDeltaQP.sqrt().multiply(rf);
  386.         return FastMath.copySign(1.0, x.getReal()) < 0 ?
  387.                LegendreEllipticIntegral.bigK(getM()).multiply(2).subtract(positive) :
  388.                positive;
  389.      }

  390. }