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.analysis.differentiation;
18  
19  import org.hipparchus.exception.LocalizedCoreFormats;
20  import org.hipparchus.exception.MathIllegalArgumentException;
21  import org.hipparchus.util.FastMath;
22  import org.hipparchus.util.FieldSinCos;
23  import org.hipparchus.util.FieldSinhCosh;
24  import org.hipparchus.util.MathArrays;
25  import org.hipparchus.util.MathUtils;
26  import org.hipparchus.util.SinCos;
27  import org.hipparchus.util.SinhCosh;
28  
29  /** Class representing both the value and the differentials of a function.
30   * <p>This class is a stripped-down version of {@link DerivativeStructure}
31   * with only one {@link DerivativeStructure#getFreeParameters() free parameter}
32   * and {@link DerivativeStructure#getOrder() derivation order} also limited to two.
33   * It should have less overhead than {@link DerivativeStructure} in its domain.</p>
34   * <p>This class is an implementation of Rall's numbers. Rall's numbers are an
35   * extension to the real numbers used throughout mathematical expressions; they hold
36   * the derivative together with the value of a function.</p>
37   * <p>{@link UnivariateDerivative2} instances can be used directly thanks to
38   * the arithmetic operators to the mathematical functions provided as
39   * methods by this class (+, -, *, /, %, sin, cos ...).</p>
40   * <p>Implementing complex expressions by hand using these classes is
41   * a tedious and error-prone task but has the advantage of having no limitation
42   * on the derivation order despite not requiring users to compute the derivatives by
43   * themselves.</p>
44   * <p>Instances of this class are guaranteed to be immutable.</p>
45   * @see DerivativeStructure
46   * @see UnivariateDerivative2
47   * @see Gradient
48   * @see FieldDerivativeStructure
49   * @see FieldUnivariateDerivative2
50   * @see FieldUnivariateDerivative2
51   * @see FieldGradient
52   * @since 1.7
53   */
54  public class UnivariateDerivative2 extends UnivariateDerivative<UnivariateDerivative2> {
55  
56      /** The constant value of π as a {@code UnivariateDerivative2}.
57       * @since 2.0
58       */
59      public static final UnivariateDerivative2 PI = new UnivariateDerivative2(FastMath.PI, 0.0, 0.0);
60  
61      /** Serializable UID. */
62      private static final long serialVersionUID = 20200520L;
63  
64      /** Value of the function. */
65      private final double f0;
66  
67      /** First derivative of the function. */
68      private final double f1;
69  
70      /** Second derivative of the function. */
71      private final double f2;
72  
73      /** Build an instance with values and derivative.
74       * @param f0 value of the function
75       * @param f1 first derivative of the function
76       * @param f2 second derivative of the function
77       */
78      public UnivariateDerivative2(final double f0, final double f1, final double f2) {
79          this.f0 = f0;
80          this.f1 = f1;
81          this.f2 = f2;
82      }
83  
84      /** Build an instance from a {@link DerivativeStructure}.
85       * @param ds derivative structure
86       * @exception MathIllegalArgumentException if either {@code ds} parameters
87       * is not 1 or {@code ds} order is not 2
88       */
89      public UnivariateDerivative2(final DerivativeStructure ds) throws MathIllegalArgumentException {
90          MathUtils.checkDimension(ds.getFreeParameters(), 1);
91          MathUtils.checkDimension(ds.getOrder(), 2);
92          this.f0 = ds.getValue();
93          this.f1 = ds.getPartialDerivative(1);
94          this.f2 = ds.getPartialDerivative(2);
95      }
96  
97      /** {@inheritDoc} */
98      @Override
99      public UnivariateDerivative2 newInstance(final double value) {
100         return new UnivariateDerivative2(value, 0.0, 0.0);
101     }
102 
103     /** {@inheritDoc} */
104     @Override
105     public double getReal() {
106         return getValue();
107     }
108 
109     /** {@inheritDoc} */
110     @Override
111     public double getValue() {
112         return f0;
113     }
114 
115     /** {@inheritDoc} */
116     @Override
117     public double getDerivative(final int n) {
118         switch (n) {
119             case 0 :
120                 return f0;
121             case 1 :
122                 return f1;
123             case 2 :
124                 return f2;
125             default :
126                 throw new MathIllegalArgumentException(LocalizedCoreFormats.DERIVATION_ORDER_NOT_ALLOWED, n);
127         }
128     }
129 
130     /** {@inheritDoc} */
131     @Override
132     public int getOrder() {
133         return 2;
134     }
135 
136     /** Get the first derivative.
137      * @return first derivative
138      * @see #getValue()
139      * @see #getSecondDerivative()
140      */
141     public double getFirstDerivative() {
142         return f1;
143     }
144 
145     /** Get the second derivative.
146      * @return second derivative
147      * @see #getValue()
148      * @see #getFirstDerivative()
149      */
150     public double getSecondDerivative() {
151         return f2;
152     }
153 
154     /** {@inheritDoc} */
155     @Override
156     public DerivativeStructure toDerivativeStructure() {
157         return getField().getConversionFactory().build(f0, f1, f2);
158     }
159 
160     /** {@inheritDoc} */
161     @Override
162     public UnivariateDerivative2 add(final double a) {
163         return new UnivariateDerivative2(f0 + a, f1, f2);
164     }
165 
166     /** {@inheritDoc} */
167     @Override
168     public UnivariateDerivative2 add(final UnivariateDerivative2 a) {
169         return new UnivariateDerivative2(f0 + a.f0, f1 + a.f1, f2 + a.f2);
170     }
171 
172     /** {@inheritDoc} */
173     @Override
174     public UnivariateDerivative2 subtract(final double a) {
175         return new UnivariateDerivative2(f0 - a, f1, f2);
176     }
177 
178     /** {@inheritDoc} */
179     @Override
180     public UnivariateDerivative2 subtract(final UnivariateDerivative2 a) {
181         return new UnivariateDerivative2(f0 - a.f0, f1 - a.f1, f2 - a.f2);
182     }
183 
184     /** {@inheritDoc} */
185     @Override
186     public UnivariateDerivative2 multiply(final int n) {
187         return new UnivariateDerivative2(f0 * n, f1 * n, f2 * n);
188     }
189 
190     /** {@inheritDoc} */
191     @Override
192     public UnivariateDerivative2 multiply(final double a) {
193         return new UnivariateDerivative2(f0 * a, f1 * a, f2 * a);
194     }
195 
196     /** {@inheritDoc} */
197     @Override
198     public UnivariateDerivative2 multiply(final UnivariateDerivative2 a) {
199         return new UnivariateDerivative2(f0 * a.f0,
200                                          MathArrays.linearCombination(f1, a.f0, f0, a.f1),
201                                          MathArrays.linearCombination(f2, a.f0, 2 * f1, a.f1, f0, a.f2));
202     }
203 
204     /** {@inheritDoc} */
205     @Override
206     public UnivariateDerivative2 divide(final double a) {
207         final double inv1 = 1.0 / a;
208         return new UnivariateDerivative2(f0 * inv1, f1 * inv1, f2 * inv1);
209     }
210 
211     /** {@inheritDoc} */
212     @Override
213     public UnivariateDerivative2 divide(final UnivariateDerivative2 a) {
214         final double inv1 = 1.0 / a.f0;
215         final double inv2 = inv1 * inv1;
216         final double inv3 = inv1 * inv2;
217         return new UnivariateDerivative2(f0 * inv1,
218                                          MathArrays.linearCombination(f1, a.f0, -f0, a.f1) * inv2,
219                                          MathArrays.linearCombination(f2, a.f0 * a.f0,
220                                                                       -2 * f1, a.f0 * a.f1,
221                                                                        2 * f0, a.f1 * a.f1,
222                                                                        -f0, a.f0 * a.f2) * inv3);
223     }
224 
225     /** {@inheritDoc} */
226     @Override
227     public UnivariateDerivative2 remainder(final double a) {
228         return new UnivariateDerivative2(FastMath.IEEEremainder(f0, a), f1, f2);
229     }
230 
231     /** {@inheritDoc} */
232     @Override
233     public UnivariateDerivative2 remainder(final UnivariateDerivative2 a) {
234 
235         // compute k such that lhs % rhs = lhs - k rhs
236         final double rem = FastMath.IEEEremainder(f0, a.f0);
237         final double k   = FastMath.rint((f0 - rem) / a.f0);
238 
239         return new UnivariateDerivative2(rem, f1 - k * a.f1, f2 - k * a.f2);
240 
241     }
242 
243     /** {@inheritDoc} */
244     @Override
245     public UnivariateDerivative2 negate() {
246         return new UnivariateDerivative2(-f0, -f1, -f2);
247     }
248 
249     /** {@inheritDoc} */
250     @Override
251     public UnivariateDerivative2 abs() {
252         if (Double.doubleToLongBits(f0) < 0) {
253             // we use the bits representation to also handle -0.0
254             return negate();
255         } else {
256             return this;
257         }
258     }
259 
260     /** {@inheritDoc} */
261     @Override
262     public UnivariateDerivative2 ceil() {
263         return new UnivariateDerivative2(FastMath.ceil(f0), 0.0, 0.0);
264     }
265 
266     /** {@inheritDoc} */
267     @Override
268     public UnivariateDerivative2 floor() {
269         return new UnivariateDerivative2(FastMath.floor(f0), 0.0, 0.0);
270     }
271 
272     /** {@inheritDoc} */
273     @Override
274     public UnivariateDerivative2 rint() {
275         return new UnivariateDerivative2(FastMath.rint(f0), 0.0, 0.0);
276     }
277 
278     /** {@inheritDoc} */
279     @Override
280     public UnivariateDerivative2 sign() {
281         return new UnivariateDerivative2(FastMath.signum(f0), 0.0, 0.0);
282     }
283 
284     /** {@inheritDoc} */
285     @Override
286     public UnivariateDerivative2 copySign(final UnivariateDerivative2 sign) {
287         long m = Double.doubleToLongBits(f0);
288         long s = Double.doubleToLongBits(sign.f0);
289         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
290             return this;
291         }
292         return negate(); // flip sign
293     }
294 
295     /** {@inheritDoc} */
296     @Override
297     public UnivariateDerivative2 copySign(final double sign) {
298         long m = Double.doubleToLongBits(f0);
299         long s = Double.doubleToLongBits(sign);
300         if ((m >= 0 && s >= 0) || (m < 0 && s < 0)) { // Sign is currently OK
301             return this;
302         }
303         return negate(); // flip sign
304     }
305 
306     /** {@inheritDoc} */
307     @Override
308     public int getExponent() {
309         return FastMath.getExponent(f0);
310     }
311 
312     /** {@inheritDoc} */
313     @Override
314     public UnivariateDerivative2 scalb(final int n) {
315         return new UnivariateDerivative2(FastMath.scalb(f0, n), FastMath.scalb(f1, n), FastMath.scalb(f2, n));
316     }
317 
318     /** {@inheritDoc}
319      * <p>
320      * The {@code ulp} function is a step function, hence all its derivatives are 0.
321      * </p>
322      * @since 2.0
323      */
324     @Override
325     public UnivariateDerivative2 ulp() {
326         return new UnivariateDerivative2(FastMath.ulp(f0), 0.0, 0.0);
327     }
328 
329     /** {@inheritDoc} */
330     @Override
331     public UnivariateDerivative2 hypot(final UnivariateDerivative2 y) {
332 
333         if (Double.isInfinite(f0) || Double.isInfinite(y.f0)) {
334             return new UnivariateDerivative2(Double.POSITIVE_INFINITY, 0.0, 0.0);
335         } else if (Double.isNaN(f0) || Double.isNaN(y.f0)) {
336             return new UnivariateDerivative2(Double.NaN, 0.0, 0.0);
337         } else {
338 
339             final int expX = getExponent();
340             final int expY = y.getExponent();
341             if (expX > expY + 27) {
342                 // y is neglectible with respect to x
343                 return abs();
344             } else if (expY > expX + 27) {
345                 // x is neglectible with respect to y
346                 return y.abs();
347             } else {
348 
349                 // find an intermediate scale to avoid both overflow and underflow
350                 final int middleExp = (expX + expY) / 2;
351 
352                 // scale parameters without losing precision
353                 final UnivariateDerivative2 scaledX = scalb(-middleExp);
354                 final UnivariateDerivative2 scaledY = y.scalb(-middleExp);
355 
356                 // compute scaled hypotenuse
357                 final UnivariateDerivative2 scaledH =
358                         scaledX.multiply(scaledX).add(scaledY.multiply(scaledY)).sqrt();
359 
360                 // remove scaling
361                 return scaledH.scalb(middleExp);
362 
363             }
364 
365         }
366     }
367 
368     /** {@inheritDoc} */
369     @Override
370     public UnivariateDerivative2 reciprocal() {
371         final double inv1 = 1.0 / f0;
372         final double inv2 = inv1 * inv1;
373         final double inv3 = inv1 * inv2;
374         return new UnivariateDerivative2(inv1, -f1 * inv2, MathArrays.linearCombination(2 * f1, f1, -f0, f2) * inv3);
375     }
376 
377     /** {@inheritDoc} */
378     @Override
379     public UnivariateDerivative2 compose(final double... f) {
380         MathUtils.checkDimension(f.length, getOrder() + 1);
381         return new UnivariateDerivative2(f[0],
382                                          f[1] * f1,
383                                          MathArrays.linearCombination(f[1], f2, f[2], f1 * f1));
384     }
385 
386     /** {@inheritDoc} */
387     @Override
388     public UnivariateDerivative2 sqrt() {
389         final double s0 = FastMath.sqrt(f0);
390         final double s0twice = 2. * s0;
391         final double s1 = f1 / s0twice;
392         final double s2 = (f2 - 2. * s1 * s1) / s0twice;
393         return new UnivariateDerivative2(s0, s1, s2);
394     }
395 
396     /** {@inheritDoc} */
397     @Override
398     public UnivariateDerivative2 cbrt() {
399         final double c  = FastMath.cbrt(f0);
400         final double c2 = c * c;
401         return compose(c, 1 / (3 * c2), -1 / (4.5 * c2 * f0));
402     }
403 
404     /** {@inheritDoc} */
405     @Override
406     public UnivariateDerivative2 rootN(final int n) {
407         if (n == 2) {
408             return sqrt();
409         } else if (n == 3) {
410             return cbrt();
411         } else {
412             final double r = FastMath.pow(f0, 1.0 / n);
413             final double z = n * FastMath.pow(r, n - 1);
414             return compose(r, 1 / z, (1 - n) / (z * z * r));
415         }
416     }
417 
418     /** {@inheritDoc} */
419     @Override
420     public UnivariateDerivative2Field getField() {
421         return UnivariateDerivative2Field.getInstance();
422     }
423 
424     /** Compute a<sup>x</sup> where a is a double and x a {@link UnivariateDerivative2}
425      * @param a number to exponentiate
426      * @param x power to apply
427      * @return a<sup>x</sup>
428      */
429     public static UnivariateDerivative2 pow(final double a, final UnivariateDerivative2 x) {
430         if (a == 0) {
431             return x.getField().getZero();
432         } else {
433             final double aX    = FastMath.pow(a, x.f0);
434             final double lnA   = FastMath.log(a);
435             final double aXlnA = aX * lnA;
436             return new UnivariateDerivative2(aX, aXlnA * x.f1, aXlnA * (x.f1 * x.f1 * lnA + x.f2));
437         }
438     }
439 
440     /** {@inheritDoc} */
441     @Override
442     public UnivariateDerivative2 pow(final double p) {
443         if (p == 0) {
444             return getField().getOne();
445         } else {
446             final double f0Pm2 = FastMath.pow(f0, p - 2);
447             final double f0Pm1 = f0Pm2 * f0;
448             final double f0P   = f0Pm1 * f0;
449             return compose(f0P, p * f0Pm1, p * (p - 1) * f0Pm2);
450         }
451     }
452 
453     /** {@inheritDoc} */
454     @Override
455     public UnivariateDerivative2 pow(final int n) {
456         if (n == 0) {
457             return getField().getOne();
458         } else {
459             final double f0Nm2 = FastMath.pow(f0, n - 2);
460             final double f0Nm1 = f0Nm2 * f0;
461             final double f0N   = f0Nm1 * f0;
462             return compose(f0N, n * f0Nm1, n * (n - 1) * f0Nm2);
463         }
464     }
465 
466     /** {@inheritDoc} */
467     @Override
468     public UnivariateDerivative2 pow(final UnivariateDerivative2 e) {
469         return log().multiply(e).exp();
470     }
471 
472     /** {@inheritDoc} */
473     @Override
474     public UnivariateDerivative2 exp() {
475         final double exp = FastMath.exp(f0);
476         return compose(exp, exp, exp);
477     }
478 
479     /** {@inheritDoc} */
480     @Override
481     public UnivariateDerivative2 expm1() {
482         final double exp   = FastMath.exp(f0);
483         final double expM1 = FastMath.expm1(f0);
484         return compose(expM1, exp, exp);
485     }
486 
487     /** {@inheritDoc} */
488     @Override
489     public UnivariateDerivative2 log() {
490         final double inv = 1 / f0;
491         return compose(FastMath.log(f0), inv, -inv * inv);
492     }
493 
494     /** {@inheritDoc} */
495     @Override
496     public UnivariateDerivative2 log1p() {
497         final double inv = 1 / (1 + f0);
498         return compose(FastMath.log1p(f0), inv, -inv * inv);
499     }
500 
501     /** {@inheritDoc} */
502     @Override
503     public UnivariateDerivative2 log10() {
504         final double invF0 = 1 / f0;
505         final double inv = invF0 / FastMath.log(10.0);
506         return compose(FastMath.log10(f0), inv, -inv * invF0);
507     }
508 
509     /** {@inheritDoc} */
510     @Override
511     public UnivariateDerivative2 cos() {
512         final SinCos sinCos = FastMath.sinCos(f0);
513         return compose(sinCos.cos(), -sinCos.sin(), -sinCos.cos());
514     }
515 
516     /** {@inheritDoc} */
517     @Override
518     public UnivariateDerivative2 sin() {
519         final SinCos sinCos = FastMath.sinCos(f0);
520         return compose(sinCos.sin(), sinCos.cos(), -sinCos.sin());
521     }
522 
523     /** {@inheritDoc} */
524     @Override
525     public FieldSinCos<UnivariateDerivative2> sinCos() {
526         final SinCos sinCos = FastMath.sinCos(f0);
527         return new FieldSinCos<>(compose(sinCos.sin(),  sinCos.cos(), -sinCos.sin()),
528                                  compose(sinCos.cos(), -sinCos.sin(), -sinCos.cos()));
529     }
530 
531     /** {@inheritDoc} */
532     @Override
533     public UnivariateDerivative2 tan() {
534         final double tan  = FastMath.tan(f0);
535         final double sec2 = 1 + tan * tan;
536         return compose(tan, sec2, 2 * sec2 * tan);
537     }
538 
539     /** {@inheritDoc} */
540     @Override
541     public UnivariateDerivative2 acos() {
542         final double inv = 1.0 / (1 - f0 * f0);
543         final double mS  = -FastMath.sqrt(inv);
544         return compose(FastMath.acos(f0), mS, mS * f0 * inv);
545     }
546 
547     /** {@inheritDoc} */
548     @Override
549     public UnivariateDerivative2 asin() {
550         final double inv = 1.0 / (1 - f0 * f0);
551         final double s   = FastMath.sqrt(inv);
552         return compose(FastMath.asin(f0), s, s * f0 * inv);
553     }
554 
555     /** {@inheritDoc} */
556     @Override
557     public UnivariateDerivative2 atan() {
558         final double inv = 1 / (1 + f0 * f0);
559         return compose(FastMath.atan(f0), inv, -2 * f0 * inv * inv);
560     }
561 
562     /** {@inheritDoc} */
563     @Override
564     public UnivariateDerivative2 atan2(final UnivariateDerivative2 x) {
565         final double x2    = x.f0 * x.f0;
566         final double f02   = f0 + f0;
567         final double inv   = 1.0 / (f0 * f0 + x2);
568         final double atan0 = FastMath.atan2(f0, x.f0);
569         final double atan1 = MathArrays.linearCombination(x.f0, f1, -x.f1, f0) * inv;
570         final double c     = MathArrays.linearCombination(f2, x2,
571                                                           -2 * f1, x.f0 * x.f1,
572                                                           f02, x.f1 * x.f1,
573                                                           -f0, x.f0 * x.f2) * inv;
574         return new UnivariateDerivative2(atan0, atan1, (c - f02 * atan1 * atan1) / x.f0);
575     }
576 
577     /** {@inheritDoc} */
578     @Override
579     public UnivariateDerivative2 cosh() {
580         final double c = FastMath.cosh(f0);
581         final double s = FastMath.sinh(f0);
582         return compose(c, s, c);
583     }
584 
585     /** {@inheritDoc} */
586     @Override
587     public UnivariateDerivative2 sinh() {
588         final double c = FastMath.cosh(f0);
589         final double s = FastMath.sinh(f0);
590         return compose(s, c, s);
591     }
592 
593     /** {@inheritDoc} */
594     @Override
595     public FieldSinhCosh<UnivariateDerivative2> sinhCosh() {
596         final SinhCosh sinhCosh = FastMath.sinhCosh(f0);
597         return new FieldSinhCosh<>(compose(sinhCosh.sinh(), sinhCosh.cosh(), sinhCosh.sinh()),
598                                    compose(sinhCosh.cosh(), sinhCosh.sinh(), sinhCosh.cosh()));
599     }
600 
601     /** {@inheritDoc} */
602     @Override
603     public UnivariateDerivative2 tanh() {
604         final double tanh  = FastMath.tanh(f0);
605         final double sech2 = 1 - tanh * tanh;
606         return compose(tanh, sech2, -2 * sech2 * tanh);
607     }
608 
609     /** {@inheritDoc} */
610     @Override
611     public UnivariateDerivative2 acosh() {
612         final double inv = 1 / (f0 * f0 - 1);
613         final double s   = FastMath.sqrt(inv);
614         return compose(FastMath.acosh(f0), s, -f0 * s * inv);
615     }
616 
617     /** {@inheritDoc} */
618     @Override
619     public UnivariateDerivative2 asinh() {
620         final double inv = 1 / (f0 * f0 + 1);
621         final double s   = FastMath.sqrt(inv);
622         return compose(FastMath.asinh(f0), s, -f0 * s * inv);
623     }
624 
625     /** {@inheritDoc} */
626     @Override
627     public UnivariateDerivative2 atanh() {
628         final double inv = 1 / (1 - f0 * f0);
629         return compose(FastMath.atanh(f0), inv, 2 * f0 * inv * inv);
630     }
631 
632     /** {@inheritDoc} */
633     @Override
634     public UnivariateDerivative2 toDegrees() {
635         return new UnivariateDerivative2(FastMath.toDegrees(f0), FastMath.toDegrees(f1), FastMath.toDegrees(f2));
636     }
637 
638     /** {@inheritDoc} */
639     @Override
640     public UnivariateDerivative2 toRadians() {
641         return new UnivariateDerivative2(FastMath.toRadians(f0), FastMath.toRadians(f1), FastMath.toRadians(f2));
642     }
643 
644     /** Evaluate Taylor expansion a univariate derivative.
645      * @param delta parameter offset Δx
646      * @return value of the Taylor expansion at x + Δx
647      */
648     public double taylor(final double delta) {
649         return f0 + delta * (f1 + 0.5 * delta * f2);
650     }
651 
652     /** {@inheritDoc} */
653     @Override
654     public UnivariateDerivative2 linearCombination(final UnivariateDerivative2[] a, final UnivariateDerivative2[] b) {
655 
656         // extract values and derivatives
657         final int      n  = a.length;
658         final double[] a0 = new double[n];
659         final double[] b0 = new double[n];
660         final double[] a1 = new double[2 * n];
661         final double[] b1 = new double[2 * n];
662         final double[] a2 = new double[3 * n];
663         final double[] b2 = new double[3 * n];
664         for (int i = 0; i < n; ++i) {
665             final UnivariateDerivative2 ai = a[i];
666             final UnivariateDerivative2 bi = b[i];
667             a0[i]         = ai.f0;
668             b0[i]         = bi.f0;
669             a1[2 * i]     = ai.f0;
670             a1[2 * i + 1] = ai.f1;
671             b1[2 * i]     = bi.f1;
672             b1[2 * i + 1] = bi.f0;
673             a2[3 * i]     = ai.f0;
674             a2[3 * i + 1] = ai.f1 + ai.f1;
675             a2[3 * i + 2] = ai.f2;
676             b2[3 * i]     = bi.f2;
677             b2[3 * i + 1] = bi.f1;
678             b2[3 * i + 2] = bi.f0;
679         }
680 
681         return new UnivariateDerivative2(MathArrays.linearCombination(a0, b0),
682                                          MathArrays.linearCombination(a1, b1),
683                                          MathArrays.linearCombination(a2, b2));
684 
685     }
686 
687     /** {@inheritDoc} */
688     @Override
689     public UnivariateDerivative2 linearCombination(final double[] a, final UnivariateDerivative2[] b) {
690 
691         // extract values and derivatives
692         final int      n  = b.length;
693         final double[] b0 = new double[n];
694         final double[] b1 = new double[n];
695         final double[] b2 = new double[n];
696         for (int i = 0; i < n; ++i) {
697             b0[i] = b[i].f0;
698             b1[i] = b[i].f1;
699             b2[i] = b[i].f2;
700         }
701 
702         return new UnivariateDerivative2(MathArrays.linearCombination(a, b0),
703                                          MathArrays.linearCombination(a, b1),
704                                          MathArrays.linearCombination(a, b2));
705 
706     }
707 
708     /** {@inheritDoc} */
709     @Override
710     public UnivariateDerivative2 linearCombination(final UnivariateDerivative2 a1, final UnivariateDerivative2 b1,
711                                                    final UnivariateDerivative2 a2, final UnivariateDerivative2 b2) {
712         return new UnivariateDerivative2(MathArrays.linearCombination(a1.f0, b1.f0,
713                                                                       a2.f0, b2.f0),
714                                          MathArrays.linearCombination(a1.f0, b1.f1,
715                                                                       a1.f1, b1.f0,
716                                                                       a2.f0, b2.f1,
717                                                                       a2.f1, b2.f0),
718                                          MathArrays.linearCombination(new double[] {
719                                                                           a1.f0, 2 * a1.f1, a1.f2,
720                                                                           a2.f0, 2 * a2.f1, a2.f2
721                                                                       }, new double[] {
722                                                                           b1.f2, b1.f1, b1.f0,
723                                                                           b2.f2, b2.f1, b2.f0
724                                                                       }));
725     }
726 
727     /** {@inheritDoc} */
728     @Override
729     public UnivariateDerivative2 linearCombination(final double a1, final UnivariateDerivative2 b1,
730                                                    final double a2, final UnivariateDerivative2 b2) {
731         return new UnivariateDerivative2(MathArrays.linearCombination(a1, b1.f0,
732                                                                       a2, b2.f0),
733                                          MathArrays.linearCombination(a1, b1.f1,
734                                                                       a2, b2.f1),
735                                          MathArrays.linearCombination(a1, b1.f2,
736                                                                       a2, b2.f2));
737     }
738 
739     /** {@inheritDoc} */
740     @Override
741     public UnivariateDerivative2 linearCombination(final UnivariateDerivative2 a1, final UnivariateDerivative2 b1,
742                                                    final UnivariateDerivative2 a2, final UnivariateDerivative2 b2,
743                                                    final UnivariateDerivative2 a3, final UnivariateDerivative2 b3) {
744         return new UnivariateDerivative2(MathArrays.linearCombination(a1.f0, b1.f0,
745                                                                       a2.f0, b2.f0,
746                                                                       a3.f0, b3.f0),
747                                          MathArrays.linearCombination(new double[] {
748                                                                           a1.f0, a1.f1,
749                                                                           a2.f0, a2.f1,
750                                                                           a3.f0, a3.f1
751                                                                       }, new double[] {
752                                                                           b1.f1, b1.f0,
753                                                                           b2.f1, b2.f0,
754                                                                           b3.f1, b3.f0
755                                                                       }),
756                                          MathArrays.linearCombination(new double[] {
757                                                                           a1.f0, 2 * a1.f1, a1.f2,
758                                                                           a2.f0, 2 * a2.f1, a2.f2,
759                                                                           a3.f0, 2 * a3.f1, a3.f2
760                                                                       }, new double[] {
761                                                                           b1.f2, b1.f1, b1.f0,
762                                                                           b2.f2, b2.f1, b2.f0,
763                                                                           b3.f2, b3.f1, b3.f0
764                                                                       }));
765     }
766 
767     /** {@inheritDoc} */
768     @Override
769     public UnivariateDerivative2 linearCombination(final double a1, final UnivariateDerivative2 b1,
770                                                    final double a2, final UnivariateDerivative2 b2,
771                                                    final double a3, final UnivariateDerivative2 b3) {
772         return new UnivariateDerivative2(MathArrays.linearCombination(a1, b1.f0,
773                                                                       a2, b2.f0,
774                                                                       a3, b3.f0),
775                                          MathArrays.linearCombination(a1, b1.f1,
776                                                                       a2, b2.f1,
777                                                                       a3, b3.f1),
778                                          MathArrays.linearCombination(a1, b1.f2,
779                                                                       a2, b2.f2,
780                                                                       a3, b3.f2));
781     }
782 
783     /** {@inheritDoc} */
784     @Override
785     public UnivariateDerivative2 linearCombination(final UnivariateDerivative2 a1, final UnivariateDerivative2 b1,
786                                                    final UnivariateDerivative2 a2, final UnivariateDerivative2 b2,
787                                                    final UnivariateDerivative2 a3, final UnivariateDerivative2 b3,
788                                                    final UnivariateDerivative2 a4, final UnivariateDerivative2 b4) {
789         return new UnivariateDerivative2(MathArrays.linearCombination(a1.f0, b1.f0,
790                                                                       a2.f0, b2.f0,
791                                                                       a3.f0, b3.f0,
792                                                                       a4.f0, b4.f0),
793                                          MathArrays.linearCombination(new double[] {
794                                                                           a1.f0, a1.f1,
795                                                                           a2.f0, a2.f1,
796                                                                           a3.f0, a3.f1,
797                                                                           a4.f0, a4.f1
798                                                                       }, new double[] {
799                                                                           b1.f1, b1.f0,
800                                                                           b2.f1, b2.f0,
801                                                                           b3.f1, b3.f0,
802                                                                           b4.f1, b4.f0
803                                                                       }),
804                                          MathArrays.linearCombination(new double[] {
805                                                                           a1.f0, 2 * a1.f1, a1.f2,
806                                                                           a2.f0, 2 * a2.f1, a2.f2,
807                                                                           a3.f0, 2 * a3.f1, a3.f2,
808                                                                           a4.f0, 2 * a4.f1, a4.f2
809                                                                       }, new double[] {
810                                                                           b1.f2, b1.f1, b1.f0,
811                                                                           b2.f2, b2.f1, b2.f0,
812                                                                           b3.f2, b3.f1, b3.f0,
813                                                                           b4.f2, b4.f1, b4.f0
814                                                                       }));
815     }
816 
817     /** {@inheritDoc} */
818     @Override
819     public UnivariateDerivative2 linearCombination(final double a1, final UnivariateDerivative2 b1,
820                                                    final double a2, final UnivariateDerivative2 b2,
821                                                    final double a3, final UnivariateDerivative2 b3,
822                                                    final double a4, final UnivariateDerivative2 b4) {
823         return new UnivariateDerivative2(MathArrays.linearCombination(a1, b1.f0,
824                                                                       a2, b2.f0,
825                                                                       a3, b3.f0,
826                                                                       a4, b4.f0),
827                                          MathArrays.linearCombination(a1, b1.f1,
828                                                                       a2, b2.f1,
829                                                                       a3, b3.f1,
830                                                                       a4, b4.f1),
831                                          MathArrays.linearCombination(a1, b1.f2,
832                                                                       a2, b2.f2,
833                                                                       a3, b3.f2,
834                                                                       a4, b4.f2));
835     }
836 
837     /** {@inheritDoc} */
838     @Override
839     public UnivariateDerivative2 getPi() {
840         return PI;
841     }
842 
843     /** Test for the equality of two univariate derivatives.
844      * <p>
845      * univariate derivatives are considered equal if they have the same derivatives.
846      * </p>
847      * @param other Object to test for equality to this
848      * @return true if two univariate derivatives are equal
849      */
850     @Override
851     public boolean equals(Object other) {
852 
853         if (this == other) {
854             return true;
855         }
856 
857         if (other instanceof UnivariateDerivative2) {
858             final UnivariateDerivative2 rhs = (UnivariateDerivative2) other;
859             return f0 == rhs.f0 && f1 == rhs.f1 && f2 == rhs.f2;
860         }
861 
862         return false;
863 
864     }
865 
866     /** Get a hashCode for the univariate derivative.
867      * @return a hash code value for this object
868      */
869     @Override
870     public int hashCode() {
871         return 317 - 41 * Double.hashCode(f0) + 57 * Double.hashCode(f1) - 103 * Double.hashCode(f2);
872     }
873 
874     /** {@inheritDoc}
875      * <p>
876      * Comparison performed considering that derivatives are intrinsically linked to monomials in the corresponding
877      * Taylor expansion and that the higher the degree, the smaller the term.
878      * </p>
879      * @since 3.0
880      */
881     @Override
882     public int compareTo(final UnivariateDerivative2 o) {
883         final int cF0 = Double.compare(f0, o.getReal());
884         if (cF0 == 0) {
885             final int cF1 = Double.compare(f1, o.getFirstDerivative());
886             if (cF1 == 0) {
887                 return Double.compare(f2, o.getSecondDerivative());
888             } else {
889                 return cF1;
890             }
891         } else {
892             return cF0;
893         }
894     }
895 
896 }