1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS 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  package org.orekit.propagation.semianalytical.dsst.utilities;
18  
19  import org.hipparchus.util.CombinatoricsUtils;
20  import org.hipparchus.util.FastMath;
21  import org.junit.jupiter.api.Assertions;
22  import org.junit.jupiter.api.BeforeEach;
23  import org.junit.jupiter.api.Test;
24  
25  import java.lang.reflect.Field;
26  import java.lang.reflect.InvocationTargetException;
27  import java.lang.reflect.Method;
28  
29  public class GammaMnsFunctionTest {
30  
31      int      nMax;
32      double[] fact;
33  
34      @Test
35      public void testIndex()
36          throws NoSuchMethodException, SecurityException,
37                 IllegalAccessException, IllegalArgumentException, InvocationTargetException {
38          Method indexM = GammaMnsFunction.class.getDeclaredMethod("index",
39                                                                   Integer.TYPE, Integer.TYPE, Integer.TYPE);
40          indexM.setAccessible(true);
41          int i = 0;
42          for (int n = 0; n <= nMax; ++n) {
43              for (int m = 0; m <= n; ++m) {
44                  for (int s = -n; s <= n; ++s) {
45                      Assertions.assertEquals(i++, indexM.invoke(null, m, n, s));
46                  }
47              }
48          }
49      }
50  
51      @Test
52      public void testPrecomputedRatios()
53          throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
54          Field precomputedF = GammaMnsFunction.class.getDeclaredField("PRECOMPUTED_RATIOS");
55          precomputedF.setAccessible(true);
56          new GammaMnsFunction(nMax, 0.5, +1);
57          double[] precomputed = (double[]) precomputedF.get(null);
58          int i = 0;
59          for (int n = 0; n <= nMax; ++n) {
60              for (int m = 0; m <= n; ++m) {
61                  for (int s = -n; s <= n; ++s) {
62                      // compare against naive implementation
63                      double r = naiveRatio(m, n, s);
64                      Assertions.assertEquals(r, precomputed[i++], 2.0e-14 * r);
65                  }
66              }
67          }
68      }
69  
70      @Test
71      public void testReallocate()
72          throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
73          Field precomputedF = GammaMnsFunction.class.getDeclaredField("PRECOMPUTED_RATIOS");
74          precomputedF.setAccessible(true);
75          precomputedF.set(null, new double[0]);
76          new GammaMnsFunction(nMax, 0.5, +1);
77          double[] orginalPrecomputed = (double[]) precomputedF.get(null);
78          Assertions.assertEquals((nMax + 1) * (nMax + 2) * (4 * nMax + 3) / 6, orginalPrecomputed.length);
79          new GammaMnsFunction(nMax + 3, 0.5, +1);
80          double[] reallocatedPrecomputed = (double[]) precomputedF.get(null);
81          Assertions.assertEquals((nMax + 4) * (nMax + 5) * (4 * nMax + 15) / 6, reallocatedPrecomputed.length);
82          for (int i = 0; i < orginalPrecomputed.length; ++i) {
83              Assertions.assertEquals(orginalPrecomputed[i], reallocatedPrecomputed[i],
84                                  1.0e-15 * orginalPrecomputed[i]);
85          }
86      }
87  
88      @Test
89      public void testValue() {
90          for (int bigI : new int[] { -1, +1 }) {
91              for (double gamma = 0; gamma <= 1; gamma += 1.0 / 64.0) {
92                  GammaMnsFunction gammaMNS = new GammaMnsFunction(nMax, gamma, bigI);
93                  for (int n = 0; n <= nMax; ++n) {
94                      for (int m = 0; m <= n; ++m) {
95                          for (int s = -n; s <= n; ++s) {
96                              // compare against naive implementation
97                              final double v = naiveValue(bigI, gamma, m, n, s);
98                              final double g = gammaMNS.getValue(m, n, s);
99                              if (Double.isInfinite(v)) {
100                                 Assertions.assertTrue(Double.isInfinite(g));
101                                 Assertions.assertTrue(v * g > 0);
102                             } else {
103                                 Assertions.assertEquals(v, g, 2.0e-14 * FastMath.abs(v));
104                             }
105                         }
106                     }
107                 }
108             }
109         }
110     }
111 
112     private double naiveValue(final int bigI, final double gamma,
113                                        final int m, final int n, final int s) {
114         final double f = 1 + bigI * gamma;
115         if (s <= -m) {
116             return FastMath.pow(-1, m - s ) * FastMath.pow(2, s) * FastMath.pow(f, -bigI * m);
117         } else if (s < m) {
118             return FastMath.pow(-1, m - s ) * FastMath.pow(2, -m) * naiveRatio(m, n, s) * FastMath.pow(f, bigI * s);
119         } else {
120             return  FastMath.pow(2, -s) * FastMath.pow(f, bigI * m);
121         }
122     }
123 
124     private double naiveRatio(final int m, final int n, final int s) {
125         return (CombinatoricsUtils.factorialDouble(n + m) * CombinatoricsUtils.factorialDouble(n - m)) /
126                (CombinatoricsUtils.factorialDouble(n + s) * CombinatoricsUtils.factorialDouble(n - s));
127     }
128 
129     @BeforeEach
130     public void setUp() {
131         nMax = 12;
132         fact = new double[2 * nMax + 1];
133         fact[0] = 1;
134         for (int i = 1; i < fact.length; ++i) {
135             fact[i] = i * fact[i - 1];
136         }
137     }
138 
139 }